Compare commits
178 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e921e3c12 | ||
|
|
7e2772812c | ||
|
|
9bd4f99006 | ||
|
|
0dbc586952 | ||
|
|
e09fc3f99f | ||
|
|
7dbd4976b7 | ||
|
|
66414461c4 | ||
|
|
2dd0e9150b | ||
|
|
d1b998da12 | ||
|
|
e76594f426 | ||
|
|
6553c49fbe | ||
|
|
7b256fc956 | ||
|
|
7cc88449d7 | ||
|
|
8aeb645046 | ||
|
|
f50c32778c | ||
|
|
ed38bc2cbb | ||
|
|
e8c5f546cd | ||
|
|
d3ce835956 | ||
|
|
c858734d7f | ||
|
|
97a76bd0d7 | ||
|
|
ee4e49a058 | ||
|
|
150eb06cd0 | ||
|
|
7b71d0759e | ||
|
|
f38749961b | ||
|
|
b5e5b8618e | ||
|
|
cec368ab5f | ||
|
|
9db5150b07 | ||
|
|
4dfe05ac8a | ||
|
|
97f680d738 | ||
|
|
8b79f82e29 | ||
|
|
89a8621de1 | ||
|
|
a27cf2eede | ||
|
|
3399705935 | ||
|
|
a1963fb31b | ||
|
|
1a12f487e7 | ||
|
|
f014a79065 | ||
|
|
4fbcc19ff8 | ||
|
|
7dca1c9de9 | ||
|
|
1249ce45d3 | ||
|
|
9387357854 | ||
|
|
05c4a2c30c | ||
|
|
70cf17140b | ||
|
|
9969a4db10 | ||
|
|
ec9429adcb | ||
|
|
7dafd2c08d | ||
|
|
ad4fffb519 | ||
|
|
4715bb6144 | ||
|
|
2dc682cd14 | ||
|
|
3cfac7418b | ||
|
|
3f540b7772 | ||
|
|
003f1e62b2 | ||
|
|
320ae1c922 | ||
|
|
b6f74d4305 | ||
|
|
0a670de8f1 | ||
|
|
88efb6356a | ||
|
|
254dec9e8f | ||
|
|
c35aedbd2d | ||
|
|
97ed47b8cd | ||
|
|
6f046d144f | ||
|
|
088008165d | ||
|
|
d7e0091136 | ||
|
|
7a443c16bf | ||
|
|
b5e55fd331 | ||
|
|
5016cf3e69 | ||
|
|
a672c60afd | ||
|
|
aa5513a5dd | ||
|
|
291e7bcb82 | ||
|
|
4c9c31c90b | ||
|
|
5a15ce3cd8 | ||
|
|
8f3fa2f898 | ||
|
|
afb0f5c061 | ||
|
|
3a0468dcb9 | ||
|
|
f56854127c | ||
|
|
66980a4c62 | ||
|
|
259a36fd5d | ||
|
|
8869e53daa | ||
|
|
92495ce6ce | ||
|
|
736a589b59 | ||
|
|
2814174a38 | ||
|
|
dec9475a75 | ||
|
|
7253a174a6 | ||
|
|
348d3e8578 | ||
|
|
f9f77f24c0 | ||
|
|
f82f24f6a9 | ||
|
|
91fefbf73c | ||
|
|
21ae1ff461 | ||
|
|
80b1ef6ab4 | ||
|
|
5d08d24fdf | ||
|
|
824dd22d62 | ||
|
|
b15b42ab61 | ||
|
|
6b447e1c68 | ||
|
|
0e482a4947 | ||
|
|
005c51dcf8 | ||
|
|
281443d2d2 | ||
|
|
a17ce90e07 | ||
|
|
3f6aea4ae4 | ||
|
|
563804c415 | ||
|
|
7c055d6aff | ||
|
|
3e80586620 | ||
|
|
0464afea27 | ||
|
|
298a7da3b7 | ||
|
|
9c7bfafa23 | ||
|
|
752ce62f24 | ||
|
|
f458a69781 | ||
|
|
e967cebdc8 | ||
|
|
d9a4c76a8e | ||
|
|
771b374a0b | ||
|
|
10d11a39c6 | ||
|
|
2e36a58b78 | ||
|
|
2283492c2c | ||
|
|
0d017fa362 | ||
|
|
0c6022aa2d | ||
|
|
da5a4da97b | ||
|
|
69189fce79 | ||
|
|
93b90eda5d | ||
|
|
10738a5f8b | ||
|
|
d8620b64de | ||
|
|
d0874f7f6a | ||
|
|
99c3a0525c | ||
|
|
88cc56d9d4 | ||
|
|
aa600ba8c9 | ||
|
|
69b3a983db | ||
|
|
148c38085a | ||
|
|
dc5757f7db | ||
|
|
6a7ecd005e | ||
|
|
839d7bab79 | ||
|
|
da4e1cbc81 | ||
|
|
127045bbf6 | ||
|
|
267d38f06e | ||
|
|
7db55dddbd | ||
|
|
6970984941 | ||
|
|
2ad283abb8 | ||
|
|
8091fc7dbd | ||
|
|
4054b10714 | ||
|
|
b5ef757a00 | ||
|
|
389a8bfdb8 | ||
|
|
f54ce58b21 | ||
|
|
20b33346d0 | ||
|
|
8843fac900 | ||
|
|
bb4760d2c9 | ||
|
|
48e4e50fc7 | ||
|
|
e396ba2465 | ||
|
|
2ee44a6e18 | ||
|
|
ad094ccfdc | ||
|
|
5c5ec92eb6 | ||
|
|
2857c13434 | ||
|
|
9cb00fffba | ||
|
|
b6907a5aa0 | ||
|
|
45af502520 | ||
|
|
e05d65401e | ||
|
|
2ab09ff582 | ||
|
|
c871512e87 | ||
|
|
f821cf2314 | ||
|
|
76d24340c1 | ||
|
|
e2b7789eba | ||
|
|
e6ead89cdc | ||
|
|
a97b969e6f | ||
|
|
f9c129786c | ||
|
|
e149b57fb6 | ||
|
|
82417dc230 | ||
|
|
c5ae4fc9b4 | ||
|
|
e8ba860592 | ||
|
|
62bfd162e3 | ||
|
|
80c5c9cfb0 | ||
|
|
19e3fce5b6 | ||
|
|
5aafb4228b | ||
|
|
43101b7ded | ||
|
|
88c8428ae2 | ||
|
|
1055427e33 | ||
|
|
85a3ce0769 | ||
|
|
5c682eac26 | ||
|
|
4db5104181 | ||
|
|
d18020e4bd | ||
|
|
2772cfdefc | ||
|
|
92efb3ed23 | ||
|
|
f7acd0188d | ||
|
|
7fa5cf7f7d | ||
|
|
722f2711ae |
6
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug to help improve this project
|
||||
title: "[BUG]"
|
||||
about: Report a bug to help improve Mue
|
||||
title: "[Bug]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
@@ -14,7 +14,7 @@ A clear and concise description of what the bug is.
|
||||
Steps to reproduce the behaviour:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
4. See error in console (Ctrl + Shift + I)
|
||||
3. See error in console (Ctrl + Shift + I)
|
||||
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
about: Suggest an idea for Mue
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
2
.gitignore
vendored
@@ -6,4 +6,4 @@ build/
|
||||
# Files
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
yarn.lock
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018-2019 Mue Tab
|
||||
Copyright (c) 2018-2020 Mue Tab
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
153
README.md
@@ -1,62 +1,89 @@
|
||||

|
||||
<img src="https://raw.githubusercontent.com/mue/branding/master/logo/logo_round.png" align="left" width="180px" height="180px"/>
|
||||
<img align="left" width="0" height="192px" hspace="10"/>
|
||||
|
||||
# Mue
|
||||
[](https://discord.gg/HJmmmTB)
|
||||
> <a href="https://muetab.xyz/">Mue</a>
|
||||
|
||||
Fast, open and free-to-use new tab page for most modern browsers.
|
||||
[](/LICENSE) [](https://discord.gg/zv8C9F8) []()
|
||||
<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 customizable tab page to most modern browsers
|
||||
|
||||
<br>
|
||||
|
||||
## Table of contents
|
||||
* [Screenshot](#screenshot)
|
||||
* [Features](#features)
|
||||
* [Planned Features](#planned-features)
|
||||
* [Installation](#installation)
|
||||
* [Chrome](#chrome)
|
||||
* [Firefox](#firefox)
|
||||
* [Chromium](#edge-chromium)
|
||||
* [Opera/Other](#operaother)
|
||||
* [Contributing](#development)
|
||||
* [Requirements](#requirements)
|
||||
* [Starting](#starting)
|
||||
* [Building](#building)
|
||||
* [Credits](#credits)
|
||||
* [Maintainers](#maintainers)
|
||||
* [Contributors](#contributors)
|
||||
* [Translators](#translators)
|
||||
* [Other](#other)
|
||||
|
||||
## Screenshot
|
||||

|
||||
|
||||
## Features
|
||||
* Fast and free
|
||||
* Supports multiple browsers
|
||||
* Actively developed and opensource
|
||||
* Automatically updating API (with no tracking!) with new photos, quotes and offline mode
|
||||
* ~~Multiple language support~~
|
||||
* ~~Settings feature - enable/disable features!~~
|
||||
* Search bar, ~~update modal, copy button and more!~~
|
||||
* Actively developed and open source
|
||||
* Automatically updating API (no tracking) with new photos, quotes and offline mode
|
||||
* Search bar
|
||||
* Settings - enable/disable various features and customise parts of Mue
|
||||
* Update modal, copy button and more!
|
||||
* Marketplace - download custom photo packs, quote packs, preset settings and themes made by the community!
|
||||
|
||||
*Mue has been recently rewritten with React and is missing the features that are crossed out*
|
||||
### Planned Features
|
||||
Please see our [roadmap](https://github.com/mue/mue/projects/2)
|
||||
|
||||
## Installation
|
||||
*A demo of the tab can be found [here](https://demo.muetab.xyz)*
|
||||
### Chrome
|
||||
[](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
<br>
|
||||
[Chrome Web Store](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
|
||||
Link: [Chrome Web Store](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
|
||||
Development: Read the [Development](#development) section.
|
||||
### Firefox
|
||||
[](https://addons.mozilla.org/firefox/addon/mue)
|
||||
<br>
|
||||
[Firefox Add-ons](https://addons.mozilla.org/firefox/addon/mue)
|
||||
|
||||
Link: [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/mue)
|
||||
### Edge (Chromium)
|
||||
[Microsoft Edge Addons](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja)
|
||||
|
||||
Development: Read the [Development](#development) section.
|
||||
### Opera/Other
|
||||
Link: [GitHub Releases](https://github.com/ohlookitsderpy/Mue/releases)
|
||||
[GitHub Releases](https://github.com/mue/mue/releases)
|
||||
|
||||
Development/Other: Read the [Development](#development) section.
|
||||
### Development
|
||||
##### Requirements
|
||||
#### Requirements
|
||||
<ol>
|
||||
<li><a href='https://git-scm.com'>Git</a> (optional)</li>
|
||||
<li><a href='https://nodejs.org'>Node.js</a></li>
|
||||
<li><a href='https://git-scm.com'>Git</a></li>
|
||||
<li><a href='https://nodejs.org'>Node.JS</a></li>
|
||||
<li>A suitable browser</li>
|
||||
</ol>
|
||||
<h5>Starting</h5>
|
||||
<ol>
|
||||
<li> <code>git clone https://github.com/muetab/mue</code> (If you don't have Git just go to <b>Clone or
|
||||
download</b> and click <b>Download ZIP</b>)
|
||||
<li> Open a terminal and run these commands: (in the Mue directory)
|
||||
<li> <code>yarn</code> (or <code>npm i</code>)
|
||||
<li> <code>yarn run all</code> (or <code>npm run all</code>)
|
||||
<li> Start developing! (See the sections below for how to set up a developer copy of the extension.)
|
||||
<li> Clone the repository using <code>git clone https://github.com/mue/mue.git</code>
|
||||
<li> Run <code>yarn</code> or <code>npm i</code> to install all needed dependencies
|
||||
<li> Run <code>yarn start</code> or <code>npm start</code> to start testing
|
||||
<li> Code your heart out! (See the sections below for how to build the extension)
|
||||
</ol>
|
||||
<h2>Building</h5>
|
||||
<i>This section is a work in progress, and doesn't include the manual edits you are required to do to get it working without errors.
|
||||
Once I find a method to do it automatically, I will update this section accordingly.</i>
|
||||
<h2>Building</h2>
|
||||
<details>
|
||||
<summary><b>Chrome</b> (Click to expand)</summary>
|
||||
<summary><b>Chrome/Edge (Chromium)</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<li> <code>yarn run build</code> (or <code>npm run build</code>)
|
||||
<li> Rename <code>manifest-chrome.json</code> in the "manfiest" folder to <code>manifest.json</code> in "build" (replace the one created by React)
|
||||
<li> <code>yarn run build</code> or <code>npm run build</code>
|
||||
<li> <code>yarn run chrome</code> or <code>npm run chrome</code>
|
||||
<li> Visit <code>chrome://extensions</code> in Chrome
|
||||
<li> Click <b>Load unpacked</b> (Make sure <b>Developer Mode</b> is on)
|
||||
<li> Go to the directory containing the built copy of Mue and click <b>ok</b>
|
||||
@@ -65,8 +92,8 @@ Once I find a method to do it automatically, I will update this section accordin
|
||||
<details>
|
||||
<summary><b>Opera</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<li> <code>yarn run build</code> (or <code>npm run build</code>)
|
||||
<li> Rename <code>manifest-opera.json</code> in the "manfiest" folder to <code>manifest.json</code> in "build" (replace the one created by React)
|
||||
<li> <code>yarn run build</code> or <code>npm run build</code>
|
||||
<li> <code>yarn run opera</code> or <code>npm run opera</code>
|
||||
<li> Visit <code>about://extensions</code> in Opera
|
||||
<li> Click <b>Load unpacked extension...</b> (Make sure <b>Developer Mode</b> is on)
|
||||
<li> Go to the directory containing Mue and click <b>ok</b>
|
||||
@@ -74,11 +101,9 @@ Once I find a method to do it automatically, I will update this section accordin
|
||||
</details>
|
||||
<details>
|
||||
<summary><b>Firefox</b> (Click to expand)</summary>
|
||||
<i>Note: I'm currently trying to find a better method to do this, but this works for now.</i>
|
||||
<ol>
|
||||
<li> <code>yarn run build</code> (or <code>npm run build</code>)
|
||||
<li> Rename <code>manifest-firefox.json</code> in the "manfiest" folder to <code>manifest.json</code> in "build" (replace the one created by React)
|
||||
<li> Move <code>manifest/background-opera.js</code> to <code>build/background-opera.js</code>
|
||||
<li> <code>yarn run build</code> or <code>npm run build</code>
|
||||
<li> <code>yarn run firefox</code> or <code>npm run firefox</code>
|
||||
<li> Visit <code>about:debugging#addons</code> in Firefox
|
||||
<li> Click <b>Load Temporary Add-on</b>
|
||||
<li> Go to the directory containing Mue and click on the <b>manifest.json</b>
|
||||
@@ -89,51 +114,37 @@ Once I find a method to do it automatically, I will update this section accordin
|
||||
<summary><b>Other</b> (Click to expand)</summary>
|
||||
<i>Note: To get the full new tab experience, set your browser to open the <code>index.html</code> on startup and tab open!</i>
|
||||
<ol>
|
||||
<li> <code>yarn run build</code> or <code>npm run build</code>
|
||||
<li> Open the <code>index.html</code> in your browser
|
||||
<li> Enjoy your new tab!
|
||||
</ol>
|
||||
</details>
|
||||
|
||||
## Screenshot
|
||||
*Will be updated if needed*
|
||||
|
||||

|
||||
|
||||
## Credits
|
||||
### Maintainers
|
||||
[ohlookitsderpy](https://github.com/ohlookitsderpy) (lead dev)
|
||||
[David Ralph (ohlookitsderpy)](https://github.com/ohlookitsderpy) - Founder, Lead development, Photographer <br>
|
||||
[Alex Sparkes](https://github.com/alexsparkes) - Name, Lead design, Photographer <br>
|
||||
|
||||
[TurboMarshmello](https://github.com/TurboMarshmello) (name idea, code contributions)
|
||||
### Contributors
|
||||
[Wessel Tip](https://github.com/Wessel) - Development <br>
|
||||
[Isaac (Eartharoid)](https://github.com/eartharoid) - QA, Development, Photographer <br>
|
||||
|
||||
### Translators
|
||||
English - [David Ralph (ohlookitsderpy)](https://github.com/ohlookitsderpy) & [Alex Sparkes](https://github.com/alexsparkes)
|
||||
|
||||
Dutch - [Wessel Tip](https://github.com/Wessel)
|
||||
|
||||
French - [Alex Sparkes](https://github.com/alexsparkes)
|
||||
|
||||
Norwegian - [Anders](https://github.com/FuryingFox)
|
||||
|
||||
Russian - [MrZillaGold](https://github.com/MrZillaGold)
|
||||
|
||||
### Other
|
||||
[Pexels](https://pexels.com) - Stock photos used for offline mode
|
||||
|
||||
[Opera Forum](https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14) - Portions of code to add Opera support
|
||||
|
||||
[Google Fonts](https://fonts.google.com/specimen/Lexend+Deca) - Lexend Deca font
|
||||
[Google Fonts](https://fonts.google.com) - Lexend Deca and Roboto fonts
|
||||
|
||||
### Translations
|
||||
[ohlookitsderpy](https://github.com/ohlookitsderpy) - English (Quotes and Messages)
|
||||
|
||||
[Yanderella](https://github.com/gbacretin) - Italian (Quotes and Messages)
|
||||
|
||||
Pepehound - Spanish (Quotes and Messages)
|
||||
|
||||
Candystick - Portuguese (Some Quotes)
|
||||
|
||||
[PassTheWessel](https://github.com/PassTheWessel) - Dutch (Messages)
|
||||
|
||||
[Yanderella](https://github.com/gbacretin) and [ohlookitsderpy](https://github.com/ohlookitsderpy) - French (Messages)
|
||||
|
||||
[untocodes](https://github.com/untocodes) - Finnish and German (Messages)
|
||||
|
||||
[dondish](https://github.com/dondish) - Hebrew and Russian (Messages)
|
||||
|
||||
[Roee Lupo (MrSheldon)](https://github.com/MrSheldon) - Arabic and Swedish (Messages)
|
||||
|
||||
*Feel free to pull request with other translations!*
|
||||
|
||||
and all the contributors <3
|
||||
|
||||
# License
|
||||
Code - [BSD-3-Clause](LICENSE)
|
||||
And many thanks to [Highholding](https://discord.bio/p/highholding), [Noa Shapira](#), [Roee Lupo](https://github.com/RoeeLupo), [Jeroen](#), [Glasvegas](https://twitter.com/_glasvegas), [Anders](https://github.com/FuryingFox), [Oded Shapira](https://twitter.com/dondishdev), Jacob Tyrrell and [Nikka Lai](#) for letting us use their wonderful photographs
|
||||
|
||||
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 262 KiB |
@@ -3,11 +3,16 @@
|
||||
"offline_enabled": true,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "0.7",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "./extension-icon.png"
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "index.html"
|
||||
},
|
||||
"icons": {
|
||||
"16": "icons/16x16.png",
|
||||
"48": "icons/48x48.png",
|
||||
"128": "icons/128x128.png"
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "0.7",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "./extension-icon.png"
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "index.html"
|
||||
@@ -12,4 +12,4 @@
|
||||
"chrome_settings_overrides": {
|
||||
"homepage": "index.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "0.7",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "./extension-icon.png"
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
|
||||
31
package.json
@@ -4,31 +4,38 @@
|
||||
"author": "David \"ohlookitsderpy\" Ralph <d.ralph@muetab.xyz> (https://derpyenterprises.org)",
|
||||
"maintainers": [
|
||||
"David \"ohlookitsderpy\" Ralph <d.ralph@muetab.xyz> (https://derpyenterprises.org)",
|
||||
"Alex \"TurboMarshmello\" Sparkes <a.sparkes@muetab.xyz> (https://github.com/TurboMarshmello)"
|
||||
"Alex \"TurboMarshmello\" Sparkes <a.sparkes@muetab.xyz> (https://github.com/alexsparkes)"
|
||||
],
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"repository": {
|
||||
"url": "github:muetab/mue"
|
||||
"url": "github:mue/mue"
|
||||
},
|
||||
"homepage": "https://muetab.xyz",
|
||||
"bugs": "https://github.com/muetab/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "0.7.0",
|
||||
"version": "4.1.0",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "4.7.0",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@muetab/quotes": "^1.0.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-scripts": "3.3.0"
|
||||
"detect-browser-language": "0.0.2",
|
||||
"react": "^16.13.1",
|
||||
"react-clock": "^2.4.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-modal": "^3.11.2",
|
||||
"react-toastify": "^6.0.8",
|
||||
"supports-webp": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.7.2",
|
||||
"node-sass": "^4.13.0"
|
||||
"react-scripts": "3.4.3",
|
||||
"node-sass": "^4.14.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build"
|
||||
"build": "react-scripts build",
|
||||
"chrome": "cp manifest/chrome.json build/manifest.json",
|
||||
"firefox": "cp manifest/firefox.json build/manifest.json",
|
||||
"opera": "cp manifest/opera.json build/manifest.json && cp manifest/background-opera.js build/"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
public/fonts/Roboto-Cyrillic.woff2
Normal file
BIN
public/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/icons/16x16.png
Normal file
|
After Width: | Height: | Size: 645 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
public/icons/48x48.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
10
public/icons/mue_verified.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="20" height="20" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="200" cy="200" r="200" fill="url(#paint0_linear)"/>
|
||||
<path d="M167 265.062L294.125 137.938L311 154.812L167 298.812L100.062 231.875L116.938 215L167 265.062Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="200" y1="0" x2="200" y2="400" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF5C25"/>
|
||||
<stop offset="1" stop-color="#FF456E"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 490 B |
1
public/icons/undraw_celebration.svg
Normal file
|
After Width: | Height: | Size: 45 KiB |
@@ -1,17 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<link rel='icon' type='image/png' sizes='32x32' href='./icons/32x32.png'>
|
||||
<link rel='icon' type='image/png' sizes='16x16' href='./icons/16x16.png'>
|
||||
<title>New Tab</title>
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8' />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<link rel='icon' type='image/png' sizes='32x32' href='favicon-32x32.png'>
|
||||
<link rel='icon' type='image/png' sizes='16x16' href='favicon-16x16.png'>
|
||||
<title>New Tab</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to use Mue.</noscript>
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to use Mue.</noscript>
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
131
src/App.jsx
@@ -1,28 +1,125 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
import Background from './components/Background';
|
||||
import Clock from './components/Clock';
|
||||
import Greeting from './components/Greeting';
|
||||
import Quote from './components/Quote';
|
||||
import Search from './components/Search';
|
||||
import Credit from './components/Credit';
|
||||
//import Navbar from './components/Navbar';
|
||||
import './scss/index.scss';
|
||||
|
||||
//* App
|
||||
export default class App extends React.Component {
|
||||
import Background from './components/widgets/Background';
|
||||
import Clock from './components/widgets/Clock';
|
||||
import Greeting from './components/widgets/Greeting';
|
||||
import Quote from './components/widgets/Quote';
|
||||
import Search from './components/widgets/Search';
|
||||
import Maximise from './components/widgets/Maximise';
|
||||
import Favourite from './components/widgets/Favourite';
|
||||
|
||||
import Navbar from './components/Navbar';
|
||||
|
||||
import SettingsFunctions from './modules/settingsFunctions';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import Modal from 'react-modal';
|
||||
import { merge } from './modules/merge';
|
||||
import RoomIcon from '@material-ui/icons/Room';
|
||||
|
||||
// Modals are lazy loaded as a user won't use them every time they open a tab
|
||||
const Settings = React.lazy(() => import('./components/modals/Settings'));
|
||||
const Update = React.lazy(() => import('./components/modals/Update'));
|
||||
const Marketplace = React.lazy(() => import('./components/modals/Marketplace'));
|
||||
const Addons = React.lazy(() => import('./components/modals/Addons'));
|
||||
//const Welcome = React.lazy(() => import('./components/modals/Welcome'));
|
||||
const renderLoader = () => <div></div>;
|
||||
|
||||
export default class App extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
settingsModal: false,
|
||||
updateModal: false,
|
||||
marketplaceModal: false,
|
||||
addonsModal: false,
|
||||
quickAccessmodal: false,
|
||||
welcomeModal: false
|
||||
};
|
||||
}
|
||||
|
||||
// Render all the components
|
||||
render() {
|
||||
if (!localStorage.getItem('firstRun')) SettingsFunctions.setDefaultSettings();
|
||||
|
||||
let modalClassList = 'Modal';
|
||||
if (localStorage.getItem('darkTheme') === 'true') modalClassList = 'Modal dark';
|
||||
|
||||
let overlayClassList = 'Overlay';
|
||||
if (localStorage.getItem('animations') === 'true') overlayClassList = 'Overlay modal-animation';
|
||||
|
||||
let language = require(`./translations/${localStorage.getItem('language') || 'en'}.json`);
|
||||
const en = require('./translations/en.json');
|
||||
language = merge(en, language);
|
||||
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
const style = document.createElement('link');
|
||||
style.href = theme;
|
||||
style.rel = 'stylesheet';
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Background/>
|
||||
<Search/>
|
||||
<div id='center'>
|
||||
<Greeting/>
|
||||
<Clock/>
|
||||
<Quote/>
|
||||
<Credit/>
|
||||
<ToastContainer className='toast' position='bottom-right' autoClose={2500} hideProgressBar={false} newestOnTop={true} closeOnClick rtl={false} pauseOnFocusLoss />
|
||||
<div id='center'>
|
||||
<Search language={language.search} />
|
||||
<Navbar settingsModalOpen={() => this.setState({ settingsModal: true })} updateModalOpen={() => this.setState({ updateModal: true })} />
|
||||
<Greeting language={language.greeting} />
|
||||
<Clock/>
|
||||
<Quote language={language.toasts}/>
|
||||
<div className='credits' id='credits'>
|
||||
<h1 id='photographer'>{language.credit}</h1>
|
||||
<span id='credit' style={{'display': 'none'}}></span>
|
||||
<div id='backgroundCredits' className='tooltip'>
|
||||
<RoomIcon className='locationicon'/>
|
||||
<span className='tooltiptext' id='location'/>
|
||||
</div>
|
||||
</div>
|
||||
<Maximise/>
|
||||
<Favourite/>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Modal id={'modal'} onRequestClose={() => this.setState({ settingsModal: false })} isOpen={this.state.settingsModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
<Settings
|
||||
language={language.settings}
|
||||
modalLanguage={language.modals}
|
||||
modalClose={() => this.setState({ settingsModal: false })}
|
||||
setDefaultSettings={() => SettingsFunctions.setDefaultSettings()}
|
||||
openMarketplace={() => this.setState({ marketplaceModal: true, settingsModal: false })}
|
||||
openAddons={() => this.setState({ settingsModal: false, addonsModal: true })}
|
||||
toastLanguage={language.toasts} />
|
||||
</Modal>
|
||||
<Modal onRequestClose={() => this.setState({ updateModal: false })} isOpen={this.state.updateModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
<Update
|
||||
language={language.update}
|
||||
modalClose={() => this.setState({ updateModal: false })} />
|
||||
</Modal>
|
||||
<Modal onRequestClose={() => this.setState({ marketplaceModal: false })} isOpen={this.state.marketplaceModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Marketplace
|
||||
language={language.marketplace}
|
||||
modalLanguage={language.modals}
|
||||
modalClose={() => this.setState({ marketplaceModal: false })}
|
||||
openSettings={() => this.setState({ marketplaceModal: false, settingsModal: true })}
|
||||
openAddons={() => this.setState({ marketplaceModal: false, addonsModal: true })}
|
||||
toastLanguage={language.toasts} />
|
||||
</Modal>
|
||||
<Modal onRequestClose={() => this.setState({ addonsModal: false })} isOpen={this.state.addonsModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Addons
|
||||
language={language.addons}
|
||||
marketplaceLanguage={language.marketplace}
|
||||
modalLanguage={language.modals}
|
||||
modalClose={() => this.setState({ addonsModal: false })}
|
||||
openSettings={() => this.setState({ addonsModal: false, settingsModal: true })}
|
||||
openMarketplace={() => this.setState({ addonsModal: false, marketplaceModal: true })}
|
||||
toastLanguage={language.toasts} />
|
||||
</Modal>
|
||||
{/* <Modal onRequestClose={() => this.setState({ welcomeModal: false })} isOpen={this.state.welcomeModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Welcome modalClose={() => this.setState({ welcomeModal: false })} />
|
||||
</Modal> */ }
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
|
||||
export default class Background extends React.Component {
|
||||
async setBackground() {
|
||||
try { // First we try and get an image from the API...
|
||||
let data = await fetch('https://api.muetab.xyz/getImage?category=Outdoors');
|
||||
data = await data.json();
|
||||
|
||||
document.getElementById('root').style.backgroundImage = `url(${data.file})`; // Set the background
|
||||
document.getElementById('photographer').innerText = `Photo by ${data.photographer}`; // Set the credit
|
||||
document.getElementById('location').innerText = `${data.location}`; // Set the location tooltip
|
||||
} catch (e) { // ..and if that fails we load one locally
|
||||
const photo = Math.floor(Math.random() * (20 - 1 + 1)) + 1; // There are 20 images in the offline-images folder
|
||||
document.getElementById('backgroundCredits').style.display = 'none'; // Hide the location icon
|
||||
let photographer; // Photographer credit
|
||||
switch (photo) { // Select photographer based on image file number
|
||||
default: {
|
||||
photographer = 'Unknown';
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
photographer = 'Tirachard Kumtanom';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
photographer = 'Sohail Na';
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
photographer = 'Miriam Espacio';
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
photographer = 'NO NAME';
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 13: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 14: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 15: {
|
||||
photographer = 'Pixabay';
|
||||
break;
|
||||
}
|
||||
case 20: {
|
||||
photographer = 'Fabian Wiktor';
|
||||
break;
|
||||
}
|
||||
}
|
||||
document.getElementById('photographer').innerText = `Photo by ${photographer} (Pexels)`; // Set the credit
|
||||
document.getElementById('root').style.backgroundImage = `url(../offline-images/${photo}.jpeg)`; // Set the background
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setBackground();
|
||||
}
|
||||
|
||||
render() {
|
||||
return null; // React gets annoyed if I don't put anything here or use "return;"
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
|
||||
export default class Clock extends React.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
date: '',
|
||||
ampm: '',
|
||||
};
|
||||
}
|
||||
|
||||
startTime() {
|
||||
const t = new Date(); // Get the current date
|
||||
let h = t.getHours(); // Get hours
|
||||
// const s = today.getSeconds();
|
||||
|
||||
if (h > 12) h = h - 12; // 12 hour support
|
||||
|
||||
this.setState({
|
||||
date: `${('0' + h).slice(-2)}:${('0' + t.getMinutes()).slice(-2)}`, ampm: h >= 12 ? 'AM' : 'PM'
|
||||
}); // Set time
|
||||
|
||||
this.timeout = setTimeout(() => this.startTime(), 750); // Update the clock every 750 milliseconds
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.startTime();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <h1 className='clock'>
|
||||
{this.state.date}
|
||||
<span className='ampm'>
|
||||
{this.state.ampm}
|
||||
</span>
|
||||
</h1>;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/* eslint-disable */
|
||||
//* Imports
|
||||
import RoomIcon from '@material-ui/icons/Room';
|
||||
import React from 'react';
|
||||
|
||||
export default class Credit extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className='credits'>
|
||||
{/*<h1 id='location'></h1>*/}
|
||||
<h1 id='photographer'/>
|
||||
<div id='backgroundCredits' className='tooltip'>
|
||||
<RoomIcon className='locationicon'/>
|
||||
<span className='tooltiptext' id='location'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
|
||||
export default class Greeting extends React.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
greeting: ''
|
||||
};
|
||||
}
|
||||
|
||||
getGreeting() {
|
||||
const t = new Date(); // Current date object
|
||||
|
||||
// Normal
|
||||
const h = t.getHours(); // Current hour
|
||||
|
||||
let g = 'Good evening'; // Set the default greeting string to "Good evening"
|
||||
if (h < 12) g = 'Good morning'; // If it's before 12am, set the greeting string to "Good morning"
|
||||
else if (h < 18) g = 'Good afternoon'; // If it's before 6pm, set the greeting string to "Good afternoon"
|
||||
|
||||
// Events
|
||||
const m = t.getMonth(); // Current month
|
||||
const d = t.getDate(); // Current Date
|
||||
|
||||
if (m === 0 && d === 1) g = 'Happy new year'; // If the date is January 1st, set the greeting string to "Happy new year"
|
||||
else if (m === 11 && d === 25) g = 'Merry Christmas'; // If it's December 25th, set the greeting string to "Merry Christmas"
|
||||
else if (m === 9 && d === 31) g = 'Happy Halloween'; // If it's October 31st, set the greeting string to "Happy Halloween"
|
||||
|
||||
this.setState({
|
||||
greeting: g
|
||||
}); // Set the state to the greeting string
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getGreeting();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <h1 className='greeting'>
|
||||
{this.state.greeting}
|
||||
</h1>;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
//* Imports
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
import LocalPizzaIcon from '@material-ui/icons/LocalPizza';
|
||||
import React from 'react';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
import Gear from '@material-ui/icons/Settings';
|
||||
import NewReleases from '@material-ui/icons/NewReleases';
|
||||
|
||||
export default class Navbar extends React.Component {
|
||||
export default class Navbar extends React.PureComponent {
|
||||
render() {
|
||||
let refreshHTML = <div className='navbar2'><RefreshIcon className='refreshicon' onClick={() => window.location.reload()} /></div>;
|
||||
const refresh = localStorage.getItem('refresh');
|
||||
if (refresh === 'false') refreshHTML = '';
|
||||
|
||||
return (
|
||||
<div className='navbar-container'>
|
||||
<div className='navbar1'>
|
||||
<RefreshIcon className='locationicon'/>
|
||||
<Gear className='settings-icon' onClick={this.props.settingsModalOpen} />
|
||||
</div>
|
||||
<div className='navbar2'>
|
||||
<LocalPizzaIcon className='pizzaicon'/>
|
||||
{refreshHTML}
|
||||
<div className={refresh === 'false' ? 'navbar2' : 'navbar3'}>
|
||||
<NewReleases className='refreshicon' onClick={this.props.updateModalOpen} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
import Quotes from '@muetab/quotes';
|
||||
|
||||
export default class Quote extends React.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
quote: '',
|
||||
author: ''
|
||||
};
|
||||
}
|
||||
|
||||
async getQuote() {
|
||||
try { // First we try and get a quote from the API...
|
||||
let data = await fetch('https://api.muetab.xyz/getQuote');
|
||||
data = await data.json();
|
||||
this.setState({
|
||||
quote: data.quote,
|
||||
author: data.author
|
||||
});
|
||||
} catch (e) { // ..and if that fails we load one locally
|
||||
const quote = Quotes.random(); // Get a random quote from our local package
|
||||
this.setState({
|
||||
quote: quote.quote,
|
||||
author: quote.author
|
||||
}); // Set the quote
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getQuote();
|
||||
}
|
||||
|
||||
render() {
|
||||
return [
|
||||
<h1 className='quote'>{`"${this.state.quote}"`}</h1>,
|
||||
// <i class="material-icons">perm_identity</i>,
|
||||
<h1 className='quoteauthor'>{`${this.state.author}`}</h1>,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
|
||||
// TODO: Add option to change search engine
|
||||
export default class Search extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div id='searchBar' className='searchbar'>
|
||||
<form id='searchBar' className='searchbarform' action='https://duckduckgo.com/' onSubmit={('search();')}>
|
||||
<input type='text' placeholder='Search' name='q' id='searchtext' className='searchtext'/>
|
||||
<div className='blursearcbBG'/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
189
src/components/modals/Addons.jsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import React from 'react';
|
||||
import LocalMallIcon from '@material-ui/icons/LocalMall';
|
||||
import { toast } from 'react-toastify';
|
||||
import Item from './marketplace/Item';
|
||||
import MarketplaceFunctions from '../../modules/marketplaceFunctions';
|
||||
|
||||
export default class Addons extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
installed: [],
|
||||
current_data: {
|
||||
type: '',
|
||||
name: '',
|
||||
content: {}
|
||||
},
|
||||
item_data: {
|
||||
name: 'Name',
|
||||
author: 'Author',
|
||||
description: 'Description',
|
||||
updated: '???',
|
||||
version: '1.0.0',
|
||||
icon: ''
|
||||
},
|
||||
button: <button className='removeFromMue' onClick={() => this.uninstall()}>{this.props.marketplaceLanguage.product.buttons.remove}</button>
|
||||
}
|
||||
}
|
||||
|
||||
toggle(type, type2, data) {
|
||||
if (type === 'item') {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
let info = installed.find(i => i.name === data).content;
|
||||
this.setState({
|
||||
current_data: { type: type2, name: data, content: info },
|
||||
item_data: {
|
||||
name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: MarketplaceFunctions.urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('item').style.display = 'block';
|
||||
document.getElementById('marketplace').style.display = 'none';
|
||||
} else {
|
||||
this.setState({
|
||||
button: <button className='removeFromMue' onClick={() => this.uninstall()}>{this.props.marketplaceLanguage.product.buttons.remove}</button>
|
||||
});
|
||||
document.getElementById('marketplace').style.display = 'block';
|
||||
document.getElementById('item').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
let type, data = this.state.current_data.type;
|
||||
if (data === undefined) data = this.state.current_data.content.data.type;
|
||||
switch (data) {
|
||||
case 'photos':
|
||||
type = 'photo_packs';
|
||||
break;
|
||||
case 'quotes':
|
||||
type = 'quote_packs';
|
||||
break;
|
||||
default:
|
||||
type = this.state.current_data.type;
|
||||
break;
|
||||
}
|
||||
MarketplaceFunctions.uninstall(this.state.current_data.name, type);
|
||||
toast(this.props.toastLanguage.removed);
|
||||
this.setState({
|
||||
button: '',
|
||||
installed: JSON.parse(localStorage.getItem('installed'))
|
||||
});
|
||||
}
|
||||
|
||||
install(input) {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
let button;
|
||||
|
||||
const installStuff = () => {
|
||||
installed.push({
|
||||
content: {
|
||||
updated: 'Unpublished',
|
||||
data: input
|
||||
}
|
||||
});
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
toast(this.props.toastLanguage.installed);
|
||||
button = <button className='removeFromMue' onClick={() => this.uninstall()}>{this.props.marketplaceLanguage.product.buttons.remove}</button>;
|
||||
this.setState({
|
||||
button: button,
|
||||
installed: JSON.parse(localStorage.getItem('installed'))
|
||||
});
|
||||
}
|
||||
|
||||
switch (input.type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
let oldSettings = [];
|
||||
for (const key of Object.keys(localStorage)) oldSettings.push({name: key, value: localStorage.getItem(key)});
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
input.settings.forEach(element => localStorage.setItem(element.name, element.value));
|
||||
installStuff();
|
||||
break;
|
||||
case 'photos':
|
||||
localStorage.setItem('photo_packs', JSON.stringify(input.photos));
|
||||
installStuff();
|
||||
break;
|
||||
case 'theme':
|
||||
localStorage.setItem('theme', input.theme);
|
||||
installStuff();
|
||||
break;
|
||||
case 'quote_packs':
|
||||
if (input.quote_api) localStorage.setItem('quote_api', JSON.stringify(input.quote_api));
|
||||
localStorage.setItem('quote_packs', JSON.stringify(input.quotes));
|
||||
installStuff();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.getElementById('file-input').onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file.type !== 'application/json') return console.error(`expected json, got ${file.type}`);
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
|
||||
reader.onload = (readerEvent) => {
|
||||
const content = JSON.parse(readerEvent.target.result);
|
||||
this.install(content);
|
||||
};
|
||||
};
|
||||
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
this.setState({ installed: JSON.parse(localStorage.getItem('installed')) });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = <div className='items'>
|
||||
{this.state.installed.map((item) =>
|
||||
<div className='item' onClick={() => this.toggle('item', item.type, item.name)}>
|
||||
<img alt='icon' src={item.content.data.icon_url} />
|
||||
<div className='details'>
|
||||
<h4>{item.content.data.name}</h4>
|
||||
<p>{item.content.data.author}</p>
|
||||
</div>
|
||||
</div>)}
|
||||
</div>;
|
||||
|
||||
if (this.state.installed.length === 0) {
|
||||
content = <div className='items'>
|
||||
<div className='emptyMessage'>
|
||||
<LocalMallIcon />
|
||||
<h1>{this.props.language.empty.title}</h1>
|
||||
<p className='description'>{this.props.language.empty.description}</p>
|
||||
<button className='goToMarket' onClick={this.props.openMarketplace}>{this.props.language.empty.button}</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div className='content'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1>{this.props.modalLanguage.title}</h1>
|
||||
<div className='tab'>
|
||||
<button className='tablinks' onClick={this.props.openMarketplace}>{this.props.modalLanguage.marketplace}</button>
|
||||
<button className='tablinks' id='active'>{this.props.modalLanguage.addons}</button>
|
||||
<button className='tablinks' onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
|
||||
</div>
|
||||
<div id='marketplace'>
|
||||
<input id='file-input' type='file' name='name' className='hidden' />
|
||||
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>{this.props.language.sideload}</button>
|
||||
<h1>{this.props.language.added}</h1>
|
||||
{content}
|
||||
</div>
|
||||
<Item button={this.state.button} data={this.state.item_data} function={() => this.toggle()} language={this.props.marketplaceLanguage.product} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
252
src/components/modals/Marketplace.jsx
Normal file
@@ -0,0 +1,252 @@
|
||||
import React from 'react';
|
||||
import WifiOffIcon from '@material-ui/icons/WifiOff';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
import { toast } from 'react-toastify';
|
||||
import Item from './marketplace/Item';
|
||||
import MarketplaceFunctions from '../../modules/marketplaceFunctions';
|
||||
import * as Constants from '../../modules/constants';
|
||||
import Items from './marketplace/Items';
|
||||
|
||||
export default class Marketplace extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
themes: [],
|
||||
settings: [],
|
||||
photo_packs: [],
|
||||
quote_packs: [],
|
||||
see_more: [],
|
||||
see_more_type: '',
|
||||
current_data: {
|
||||
type: '',
|
||||
name: '',
|
||||
content: {}
|
||||
},
|
||||
button: '',
|
||||
featured: {},
|
||||
done: false,
|
||||
item_data: {
|
||||
name: 'Name',
|
||||
author: 'Author',
|
||||
description: 'Description',
|
||||
updated: '???',
|
||||
version: '1.0.0',
|
||||
icon: ''
|
||||
}
|
||||
}
|
||||
|
||||
this.offlineHTML = <div className='content'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1>{this.props.modalLanguage.title}</h1>
|
||||
<div className='tab'>
|
||||
<button className='tablinks' id='active'>{this.props.modalLanguage.marketplace}</button>
|
||||
<button className='tablinks' onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
|
||||
<button className='tablinks'
|
||||
onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
|
||||
</div>
|
||||
<div id='marketplace'>
|
||||
<div className='emptyMessage' style={{'marginTop': '20px', 'transform': 'translateY(80%)'}}>
|
||||
<WifiOffIcon />
|
||||
<h1>{this.props.language.offline.title}</h1>
|
||||
<p className='description'>{this.props.language.offline.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
async toggle(type, type2, data) {
|
||||
if (type === 'seemore') {
|
||||
document.getElementById('marketplace').style.display = 'none';
|
||||
document.getElementById('seemore').style.display = 'block';
|
||||
return this.setState({
|
||||
see_more: this.state[type2],
|
||||
see_more_type: type2
|
||||
});
|
||||
}
|
||||
|
||||
if (type === 'item') {
|
||||
let info;
|
||||
try {
|
||||
info = await (await fetch(`${Constants.MARKETPLACE_URL}/item/${type2}/${data}`)).json();
|
||||
} catch (e) {
|
||||
return toast(this.props.toastLanguage.error);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
current_data: { type: type2, name: data, content: info },
|
||||
item_data: {
|
||||
name: info.data.name,
|
||||
author: info.data.author,
|
||||
description: MarketplaceFunctions.urlParser(info.data.description.replace(/\n/g, '<br>')),
|
||||
updated: info.updated,
|
||||
version: info.data.version,
|
||||
icon: info.data.screenshot_url
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('marketplace').style.display = 'none';
|
||||
document.getElementById('seemore').style.display = 'none';
|
||||
document.getElementById('item').style.display = 'block';
|
||||
|
||||
let button = <button className='addToMue' onClick={() => this.install()}>{this.props.language.product.buttons.addtomue}</button>;
|
||||
const installed = JSON.parse(localStorage.getItem('installed'));
|
||||
if (installed.some(item => item.name === data)) button = <button className='removeFromMue' onClick={() => this.uninstall()}>{this.props.language.product.buttons.remove}</button>;
|
||||
this.setState({ button: button });
|
||||
} else {
|
||||
document.getElementById('marketplace').style.display = 'block';
|
||||
document.getElementById('item').style.display = 'none';
|
||||
document.getElementById('seemore').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async getItems() {
|
||||
const data = await (await fetch(Constants.MARKETPLACE_URL + '/all')).json();
|
||||
const featured = await (await fetch(Constants.MARKETPLACE_URL + '/featured')).json();
|
||||
this.setState({
|
||||
themes: data.data.themes,
|
||||
settings: data.data.settings,
|
||||
photo_packs: data.data.photo_packs,
|
||||
quote_packs: data.data.quote_packs,
|
||||
see_more: data.data.photo_packs,
|
||||
featured: featured.data,
|
||||
done: true
|
||||
});
|
||||
}
|
||||
|
||||
install() {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
let button;
|
||||
|
||||
const installStuff = () => {
|
||||
installed.push(this.state.current_data);
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
toast(this.props.toastLanguage.installed);
|
||||
button = <button className='removeFromMue' onClick={() => this.uninstall()}>{this.props.language.product.buttons.remove}</button>;
|
||||
this.setState({ button: button });
|
||||
}
|
||||
|
||||
switch (this.state.current_data.type) {
|
||||
case 'settings':
|
||||
localStorage.removeItem('backup_settings');
|
||||
let oldSettings = [];
|
||||
for (const key of Object.keys(localStorage)) oldSettings.push({name: key, value: localStorage.getItem(key)});
|
||||
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
|
||||
this.state.current_data.content.data.settings.forEach(element => localStorage.setItem(element.name, element.value));
|
||||
installStuff();
|
||||
break;
|
||||
case 'photo_packs':
|
||||
localStorage.setItem('photo_packs', JSON.stringify(this.state.current_data.content.data.photos));
|
||||
installStuff();
|
||||
break;
|
||||
case 'theme':
|
||||
localStorage.setItem('theme', this.state.current_data.content.data.theme);
|
||||
installStuff();
|
||||
break;
|
||||
case 'quote_packs':
|
||||
if (this.state.current_data.content.data.quote_api) localStorage.setItem('quote_api', JSON.stringify(this.state.current_data.content.data.quote_api));
|
||||
localStorage.setItem('quote_packs', JSON.stringify(this.state.current_data.content.data.quotes));
|
||||
installStuff();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
MarketplaceFunctions.uninstall(this.state.current_data.name, this.state.current_data.type);
|
||||
toast(this.props.toastLanguage.removed);
|
||||
this.setState({
|
||||
button: <button className='addToMue' onClick={() => this.install()}>{this.props.language.product.buttons.addtomue}</button>
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
if (navigator.onLine === false) return;
|
||||
this.getItems();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (navigator.onLine === false) return this.offlineHTML;
|
||||
if (this.state.done === false) {
|
||||
return <div className='content'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1>{this.props.modalLanguage.title}</h1>
|
||||
<div className='tab'>
|
||||
<button className='tablinks' id='active'>{this.props.modalLanguage.marketplace}</button>
|
||||
<button className='tablinks' onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
|
||||
<button className='tablinks'
|
||||
onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
|
||||
</div>
|
||||
<div id='marketplace'>
|
||||
<div className='emptyMessage' style={{'marginTop': '20px', 'transform': 'translateY(80%)'}}>
|
||||
<h1>Loading...</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div className='content'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1>{this.props.modalLanguage.title}</h1>
|
||||
<div className='tab'>
|
||||
<button className='tablinks' id='active'>{this.props.modalLanguage.marketplace}</button>
|
||||
<button className='tablinks' onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
|
||||
<button className='tablinks'
|
||||
onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
|
||||
</div>
|
||||
<div id='marketplace'>
|
||||
<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={() => window.location.href = this.state.featured.buttonLink}>{this.state.featured.buttonText}</button>
|
||||
</div>
|
||||
<Items
|
||||
title={this.props.language.photo_packs}
|
||||
seeMoreTitle={this.props.language.see_more}
|
||||
items={this.state.photo_packs.slice(0, 3)}
|
||||
toggleFunction={(input) => this.toggle('item', 'photo_packs', input)}
|
||||
seeMoreFunction={() => this.toggle('seemore', 'photo_packs')} />
|
||||
<Items
|
||||
title={this.props.language.preset_settings}
|
||||
seeMoreTitle={this.props.language.see_more}
|
||||
items={this.state.settings.slice(0, 3)}
|
||||
toggleFunction={(input) => this.toggle('item', 'settings', input)}
|
||||
seeMoreFunction={() => this.toggle('seemore', 'settings')} />
|
||||
<Items
|
||||
title={this.props.language.quote_packs}
|
||||
seeMoreTitle={this.props.language.see_more}
|
||||
items={this.state.quote_packs.slice(0, 3)}
|
||||
toggleFunction={(input) => this.toggle('item', 'quote_packs', input)}
|
||||
seeMoreFunction={() => this.toggle('seemore', 'quote_packs')} />
|
||||
<Items
|
||||
title={this.props.language.themes}
|
||||
seeMoreTitle={this.props.language.see_more}
|
||||
items={this.state.themes.slice(0, 3)}
|
||||
toggleFunction={(input) => this.toggle('item', 'theme', input)}
|
||||
seeMoreFunction={() => this.toggle('seemore', 'themes')} />
|
||||
</div>
|
||||
<Item
|
||||
button={this.state.button}
|
||||
data={this.state.item_data}
|
||||
content={this.state.current_data}
|
||||
function={() => this.toggle()} language={this.props.language.product}
|
||||
/>
|
||||
<div id='seemore'>
|
||||
<ArrowBackIcon className='backArrow' onClick={() => this.toggle()} />
|
||||
<Items
|
||||
title={this.props.language.see_more}
|
||||
seeMoreTitle={this.props.language.see_more}
|
||||
toggleFunction={(input) => this.toggle('item', this.state.see_more_type, input)}
|
||||
items={this.state.see_more}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
113
src/components/modals/Settings.jsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React from 'react';
|
||||
import SettingsFunctions from '../../modules/settingsFunctions';
|
||||
import Checkbox from './settings/Checkbox';
|
||||
import Slider from './settings/Slider';
|
||||
import Section from './settings/Section';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import BackgroundSettings from './settings/sections/BackgroundSettings';
|
||||
import ExperimentalSettings from './settings/sections/ExperimentalSettings';
|
||||
import SearchSettings from './settings/sections/SearchSettings';
|
||||
import LanguageSettings from './settings/sections/LanguageSettings';
|
||||
|
||||
export default class Settings extends React.PureComponent {
|
||||
resetGreeting() {
|
||||
document.getElementById('greetingName').value = '';
|
||||
toast(this.props.toastLanguage.reset);
|
||||
}
|
||||
|
||||
updateCurrent() {
|
||||
document.getElementById('greetingName').value = localStorage.getItem('greetingName');
|
||||
document.getElementById('language').value = localStorage.getItem('language');
|
||||
|
||||
if (localStorage.getItem('darkTheme') === 'true') {
|
||||
const choices = document.getElementsByClassName('choices');
|
||||
for (let i = 0; i < choices.length; i++) choices[i].style.backgroundColor = '#2f3542';
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateCurrent();
|
||||
|
||||
document.getElementById('file-input').onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file.type !== 'application/json') return console.error(`expected json, got ${file.type}`);
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
|
||||
reader.onload = (readerEvent) => {
|
||||
const content = JSON.parse(readerEvent.target.result);
|
||||
for (const key of Object.keys(content)) localStorage.setItem(key, content[key]);
|
||||
toast(this.props.toastLanguage.imported);
|
||||
};
|
||||
};
|
||||
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
|
||||
document.getElementById('center').classList.toggle('backgroundEffects');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='content'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1>{this.props.modalLanguage.title}</h1>
|
||||
<div className='tab'>
|
||||
<button className='tablinks' onClick={this.props.openMarketplace}>{this.props.modalLanguage.marketplace}</button>
|
||||
<button className='tablinks' onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
|
||||
<button className='tablinks' id='active'>{this.props.modalLanguage.settings}</button>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div className='columns'>
|
||||
<Section title={this.props.language.time.title} name='time'>
|
||||
<Checkbox name='seconds' text={this.props.language.time.seconds} />
|
||||
<Checkbox name='24hour' text={this.props.language.time.twentyfourhour} />
|
||||
<Checkbox name='ampm' text={this.props.language.time.ampm} />
|
||||
<Checkbox name='zero' text={this.props.language.time.zero} />
|
||||
<Checkbox name='analog' text={this.props.language.time.analog} />
|
||||
</Section>
|
||||
<Section title={this.props.language.greeting.title} name='greeting'>
|
||||
<Checkbox name='events' text={this.props.language.greeting.events} />
|
||||
<Checkbox name='defaultGreetingMessage' text={this.props.language.greeting.default} />
|
||||
<ul>
|
||||
<p>{this.props.language.greeting.name} <span className='modalLink' onClick={() => this.resetGreeting()}>{this.props.language.reset}</span></p>
|
||||
<input type='text' id='greetingName'></input>
|
||||
</ul>
|
||||
</Section>
|
||||
<Section title={this.props.language.quote.title} name='quote'>
|
||||
<Checkbox name='copyButton' text={this.props.language.quote.copy} />
|
||||
<Checkbox name='tweetButton' text={this.props.language.quote.tweet} />
|
||||
</Section>
|
||||
<Section title={this.props.language.background.title} name='background'>
|
||||
<BackgroundSettings language={this.props.language} toastLanguage={this.props.toastLanguage} />
|
||||
</Section>
|
||||
<Section title={this.props.language.searchbar.title} name='searchBar'>
|
||||
<SearchSettings language={this.props.language} toastLanguage={this.props.toastLanguage} />
|
||||
</Section>
|
||||
<div className='section'>
|
||||
<h4 class='nodropdown'>{this.props.language.offline}</h4>
|
||||
<Slider name='offlineMode'/>
|
||||
</div>
|
||||
<div className='section'>
|
||||
<h4 class='nodropdown'>{this.props.language.experimental.dark}</h4>
|
||||
<Slider name='darkTheme'/>
|
||||
</div>
|
||||
<ExperimentalSettings language={this.props.language} />
|
||||
<LanguageSettings language={this.props.language} />
|
||||
|
||||
<button className='apply' onClick={() => SettingsFunctions.saveStuff()}>{this.props.language.apply}</button>
|
||||
<button className='reset' onClick={() => this.props.setDefaultSettings()}>{this.props.language.reset}</button>
|
||||
<button className='export' onClick={() => SettingsFunctions.exportSettings()}>{this.props.language.export}</button>
|
||||
<button className='import' onClick={() => document.getElementById('file-input').click()}>{this.props.language.import}</button>
|
||||
<input id='file-input' type='file' name='name' className='hidden' accept='application/json' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
53
src/components/modals/Update.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import * as Constants from '../../modules/constants';
|
||||
|
||||
export default class Update extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
title: this.props.language.title,
|
||||
date: '???',
|
||||
content: this.props.language.title,
|
||||
url: '',
|
||||
author: 'Mue'
|
||||
};
|
||||
}
|
||||
|
||||
async getUpdate() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') return this.setState({
|
||||
title: this.props.language.offline.title,
|
||||
content: this.props.language.offline.description
|
||||
});
|
||||
|
||||
try { // Get update log from the API
|
||||
const data = await (await fetch(Constants.API_URL + '/getUpdate')).json();
|
||||
this.setState({
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
date: data.published,
|
||||
image: data.image,
|
||||
url: data.url,
|
||||
author: data.author
|
||||
});
|
||||
} catch (e) { // If it fails, we send an error
|
||||
this.setState({
|
||||
title: this.props.language.error.title,
|
||||
content: this.props.language.error.description
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className='updateContent'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<h1 style={{ 'marginBottom':'-10px' }}>{this.state.title}</h1>
|
||||
<h5 style={{ 'lineHeight':'0px' }}> By {this.state.author} • {this.state.date}</h5>
|
||||
<img draggable='false' src={this.state.image} alt='Update'></img>
|
||||
<p dangerouslySetInnerHTML={{__html: this.state.content + `<br/><p>Read on the blog here: <a target='_blank' class='modalLink' href='${this.state.url}'>${this.state.url}</a></p>`}}></p>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
31
src/components/modals/Welcome.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import EmailIcon from '@material-ui/icons/Email';
|
||||
|
||||
export default class Welcome extends React.PureComponent {
|
||||
render() {
|
||||
return <div className='welcomeContent'>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<div className='welcomeModalText'>
|
||||
<h2 className='subtitle'>Welcome to</h2>
|
||||
<h1 className='welcometitle'>Mue Tab</h1>
|
||||
<img alt='celebration' style={{'height': '200px', 'width': 'auto'}} src='./././icons/undraw_celebration.svg' />
|
||||
<h2 className='subtitle'>Information</h2>
|
||||
<p>Thank you for downloading Mue Tab,<br/> we hope you enjoy your time with our extension.</p>
|
||||
<h2 className='subtitle'>Tutorials</h2>
|
||||
<a href=''>General Start</a>
|
||||
<br/>
|
||||
<a href='https://blog.muetab.xyz/welcome-to-marketplace/'>Marketplace</a>
|
||||
<br/>
|
||||
<a href=''>Submitting Photos</a>
|
||||
<br/>
|
||||
<a href=''>Settings</a>
|
||||
<h2 className='subtitle'>Support</h2>
|
||||
{/* <img alt='twitter' href='https://twitter.com/getmue' className='icon' src=''/>
|
||||
<img alt='discord' href='https://discord.gg/kJsufA9' className='icon' src=''/> */}
|
||||
<EmailIcon />
|
||||
<br/>
|
||||
<button className='close' onClick={this.props.modalClose}>Close</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
57
src/components/modals/marketplace/Item.jsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
||||
|
||||
export default class Item extends React.PureComponent {
|
||||
render() {
|
||||
//if (!this.props.data.icon) return null;
|
||||
let warningHTML;
|
||||
try { // For some reason it breaks sometimes so we use try/catch
|
||||
if (this.props.content.content.data.quote_api) {
|
||||
warningHTML = <div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{this.props.language.quoteWarning.title}</li>
|
||||
<li id='updated'>{this.props.language.quoteWarning.description}</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return (
|
||||
<div id='item'>
|
||||
<br/>
|
||||
<ArrowBackIcon className='backArrow' onClick={this.props.function} />
|
||||
<br/>
|
||||
<h1>{this.props.data.name}</h1>
|
||||
{this.props.button}
|
||||
<br/><br/>
|
||||
<img alt='product' draggable={false} src={'https://external-content.duckduckgo.com/iu/?u=' + this.props.data.icon} />
|
||||
<div className='informationContainer'>
|
||||
<div className='productInformation'>
|
||||
<h4>{this.props.language.information}</h4>
|
||||
<ul>
|
||||
<br/>
|
||||
<li className='header'>{this.props.language.last_updated}</li>
|
||||
<li>{this.props.data.updated}</li>
|
||||
<br/>
|
||||
<li className='header'>{this.props.language.version}</li>
|
||||
<li>{this.props.data.version}</li>
|
||||
<br/>
|
||||
<li className='header'>{this.props.language.author}</li>
|
||||
<li>{this.props.data.author}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{this.props.language.notice.title}</li>
|
||||
<li id='updated'>{this.props.language.notice.description}</li>
|
||||
</ul>
|
||||
</div>
|
||||
{warningHTML}
|
||||
</div>
|
||||
<br/>
|
||||
<h1>{this.props.language.overview}</h1>
|
||||
<p className='description' dangerouslySetInnerHTML={{__html: this.props.data.description}}></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
25
src/components/modals/marketplace/Items.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
|
||||
export default class Items extends React.PureComponent {
|
||||
render() {
|
||||
let seeMoreHTML;
|
||||
if (this.props.seeMoreFunction) seeMoreHTML = <button className='addToMue seemore' onClick={this.props.seeMoreFunction}>{this.props.seeMoreTitle}</button>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{seeMoreHTML}
|
||||
<h1>{this.props.title}</h1>
|
||||
<div className='items'>
|
||||
{this.props.items.map((item) =>
|
||||
<div className='item' onClick={() => this.props.toggleFunction(item.name)}>
|
||||
<img alt='icon' draggable={false} src={'https://external-content.duckduckgo.com/iu/?u=' + item.icon_url} />
|
||||
<div className='details'>
|
||||
<h4>{item.display_name}</h4>
|
||||
<p>{item.author}</p>
|
||||
</div>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
30
src/components/modals/settings/Checkbox.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import SettingsFunctions from '../../../modules/settingsFunctions';
|
||||
import CheckboxUI from '@material-ui/core/Checkbox';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
|
||||
export default class Checkbox extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
checked: (localStorage.getItem(this.props.name) === 'true')
|
||||
};
|
||||
}
|
||||
|
||||
handleChange(name) {
|
||||
SettingsFunctions.setItem(name);
|
||||
this.setState({ checked: (this.state.checked === true) ? false : true });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FormControlLabel
|
||||
control={<CheckboxUI name='checkedB' color='primary' checked={this.state.checked} onChange={() => this.handleChange(this.props.name)} />}
|
||||
label={this.props.text}
|
||||
/>
|
||||
<br/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
26
src/components/modals/settings/Section.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import SettingsFunctions from '../../../modules/settingsFunctions';
|
||||
import Slider from './Slider';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
|
||||
export default class Section extends React.PureComponent {
|
||||
render() {
|
||||
let extraHTML, expandMore;
|
||||
if (this.props.children) {
|
||||
extraHTML = <li className={'extraSettings ' + this.props.title}>{this.props.children}</li>
|
||||
expandMore = <ExpandMore
|
||||
style={{ 'transition': 'all 0.5s ease 0s' }}
|
||||
className={`expandIcons expand${this.props.title}`}
|
||||
onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName(this.props.title)[0], document.getElementsByClassName('expand' + this.props.title)[0])}
|
||||
/>
|
||||
}
|
||||
return (
|
||||
<div className='section'>
|
||||
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName(this.props.title)[0], document.getElementsByClassName('expand' + this.props.title)[0])}>{this.props.title}</h4>
|
||||
{expandMore}
|
||||
<Slider name={this.props.name} />
|
||||
{extraHTML}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
26
src/components/modals/settings/Slider.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import SettingsFunctions from '../../../modules/settingsFunctions';
|
||||
|
||||
export default class Slider extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
checked: (localStorage.getItem(this.props.name) === 'true')
|
||||
};
|
||||
}
|
||||
|
||||
handleChange(name) {
|
||||
SettingsFunctions.setItem(name);
|
||||
this.setState({ checked: (this.state.checked === true) ? false : true });
|
||||
}
|
||||
|
||||
render() {
|
||||
let setText = (this.props.override) ? this.props.override : this.props.name;
|
||||
return (
|
||||
<label className='switch'>
|
||||
<input type='checkbox' checked={this.state.checked} onChange={() => this.handleChange(setText)} />
|
||||
<span className='slider'></span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class BackgroundSettings extends React.PureComponent {
|
||||
resetItem(key) {
|
||||
switch (key) {
|
||||
case 'customBackgroundColour':
|
||||
localStorage.setItem('customBackgroundColour', '');
|
||||
document.getElementById('customBackgroundHex').textContent = 'Disabled';
|
||||
break;
|
||||
case 'customBackground': document.getElementById('customBackground').value = ''; break;
|
||||
case 'blur':
|
||||
localStorage.setItem('blur', 0);
|
||||
document.getElementById('blurRange').value = 0;
|
||||
document.getElementById('blurAmount').innerText = '0';
|
||||
break;
|
||||
case 'brightness':
|
||||
localStorage.setItem('brightness', 100);
|
||||
document.getElementById('brightnessRange').value = 100;
|
||||
document.getElementById('brightnessAmount').innerText = '100';
|
||||
break;
|
||||
default: toast('resetItem requires a key!');
|
||||
}
|
||||
toast(this.props.toastLanguage.reset);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.getElementById('bg-input').onchange = (e) => {
|
||||
const reader = new FileReader();
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (file.size > 2000000) return toast('File is over 2MB', '#ff0000', '#ffffff');
|
||||
|
||||
reader.addEventListener('load', (e) => {
|
||||
localStorage.setItem('customBackground', e.target.result);
|
||||
document.getElementById('customBackground').src = e.target.result;
|
||||
document.getElementById('customBackground').value = e.target.result;
|
||||
});
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const hex = localStorage.getItem('customBackgroundColour');
|
||||
if (hex !== '') {
|
||||
document.getElementById('customBackgroundColour').value = hex;
|
||||
document.getElementById('customBackgroundHex').innerText = hex;
|
||||
} else document.getElementById('customBackgroundHex').innerText = 'Disabled';
|
||||
|
||||
const blur = localStorage.getItem('blur');
|
||||
const brightness = localStorage.getItem('brightness');
|
||||
|
||||
document.getElementById('blurAmount').innerText = blur;
|
||||
document.getElementById('blurRange').value = blur;
|
||||
|
||||
document.getElementById('brightnessAmount').innerText = brightness;
|
||||
document.getElementById('brightnessRange').value = brightness;
|
||||
|
||||
document.getElementById('customBackground').value = localStorage.getItem('customBackground');
|
||||
document.getElementById('backgroundAPI').value = localStorage.getItem('backgroundAPI');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<ul>
|
||||
<label htmlFor='backgroundapi'>{this.props.language.background.API} </label>
|
||||
<label className='dropdown'>
|
||||
<select className='select-css' name='backgroundapi' id='backgroundAPI' onChange={() => localStorage.setItem('backgroundAPI', document.getElementById('backgroundAPI').value)}>
|
||||
<option className='choices' value='mue'>Mue</option>
|
||||
<option className='choices' value='unsplash'>Unsplash</option>
|
||||
</select>
|
||||
</label>
|
||||
</ul>
|
||||
<ul>
|
||||
<p>{this.props.language.background.blur} (<span id='blurAmount'></span>%) <span className='modalLink' onClick={() => this.resetItem('blur')}>{this.props.language.reset}</span></p>
|
||||
<input className='range' type='range' min='0' max='100' id='blurRange' onInput={() => document.getElementById('blurAmount').innerText = document.getElementById('blurRange').value} />
|
||||
</ul>
|
||||
<ul>
|
||||
<p>{this.props.language.background.brightness} (<span id='brightnessAmount'></span>%) <span className='modalLink' onClick={() => this.resetItem('brightness')}>{this.props.language.reset}</span></p>
|
||||
<input className='range' type='range' min='0' max='100' id='brightnessRange' onInput={() => document.getElementById('brightnessAmount').innerText = document.getElementById('brightnessRange').value} />
|
||||
</ul>
|
||||
<ul>
|
||||
<p>{this.props.language.background.customURL} <span className='modalLink' onClick={() => this.resetItem('customBackground')}>{this.props.language.reset}</span></p>
|
||||
<input type='text' id='customBackground'></input>
|
||||
</ul>
|
||||
<ul>
|
||||
<p>{this.props.language.background.custombackground} <span className='modalLink' onClick={() => this.resetItem('customBackground')}>{this.props.language.reset}</span></p>
|
||||
<button className='uploadbg' onClick={() => document.getElementById('bg-input').click()}>{this.props.language.background.upload}</button>
|
||||
<input id='bg-input' type='file' name='name' className='hidden' accept='image/jpeg, image/png, image/webp, image/webm, image/gif' />
|
||||
</ul>
|
||||
<ul>
|
||||
<p>{this.props.language.background.customcolour} <span className='modalLink' onClick={() => this.resetItem('customBackgroundColour')}>{this.props.language.reset}</span></p>
|
||||
<input name='colour' type='color' id='customBackgroundColour' onChange={() => document.getElementById('customBackgroundHex').innerText = document.getElementById('customBackgroundColour').value}></input>
|
||||
<label htmlFor='colour' id='customBackgroundHex'>#00000</label>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import Checkbox from '../Checkbox';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import SettingsFunctions from '../../../../modules/settingsFunctions';
|
||||
|
||||
export default class ExperimentalSettings extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[5], document.getElementsByClassName('expandIcons')[5])}>{this.props.language.experimental.title}</h4>
|
||||
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[5], document.getElementsByClassName('expandIcons')[5])} />
|
||||
<li className='extraSettings'>
|
||||
<Checkbox name='webp' text={this.props.language.experimental.webp} />
|
||||
<Checkbox name='animations' text={this.props.language.experimental.animations} />
|
||||
<Checkbox name='view' text={this.props.language.experimental.view} />
|
||||
<Checkbox name='favouriteEnabled' text={this.props.language.experimental.favourite} />
|
||||
<Checkbox name='refresh' text={this.props.language.experimental.refresh} />
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
18
src/components/modals/settings/sections/LanguageSettings.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
|
||||
export default class LanguageSettings extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className='section'>
|
||||
<h4 htmlFor='language'>{this.props.language.language}</h4>
|
||||
<select className='select-css' name='language' id='language' onChange={() => localStorage.setItem('language', document.getElementById('language').value)}>
|
||||
<option className='choices' value='en'>English</option>
|
||||
<option className='choices' value='nl'>Nederlands</option>
|
||||
<option className='choices' value='fr'>Français</option>
|
||||
<option className='choices' value='no'>Norsk</option>
|
||||
<option className='choices' value='ru'>Pусский</option>
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
44
src/components/modals/settings/sections/SearchSettings.jsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import SettingsFunctions from '../../../../modules/settingsFunctions';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const searchEngines = require('../../../../modules/searchEngines.json');
|
||||
|
||||
export default class SearchSettings extends React.PureComponent {
|
||||
resetSearch() {
|
||||
localStorage.removeItem('customSearchEngine');
|
||||
document.getElementById('customSearchEngine').value = '';
|
||||
toast(this.props.toastLanguage.reset);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('searchEngine') === 'custom') {
|
||||
const input = document.getElementById('searchEngineInput');
|
||||
input.style.display = 'block';
|
||||
input.enabled = 'true';
|
||||
document.getElementById('customSearchEngine').value = localStorage.getItem('customSearchEngine');
|
||||
} else localStorage.removeItem('customSearchEngine');
|
||||
|
||||
document.getElementById('searchEngine').value = localStorage.getItem('searchEngine');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<ul>
|
||||
<label htmlFor='searchEngine'>{this.props.language.searchbar.searchengine} </label>
|
||||
<select className='select-css' name='searchEngine' id='searchEngine' onChange={() => SettingsFunctions.setSearchEngine(document.getElementById('searchEngine').value)}>
|
||||
{searchEngines.map((engine) =>
|
||||
<option className='choices' value={engine.settingsName}>{engine.name}</option>
|
||||
)}
|
||||
<option className='choices' value='custom'>Custom</option>
|
||||
</select>
|
||||
</ul>
|
||||
<ul id='searchEngineInput' style={{ display: 'none' }}>
|
||||
<p style={{"marginTop": "0px"}}>{this.props.language.searchbar.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{this.props.language.reset}</span></p>
|
||||
<input type='text' id='customSearchEngine'></input>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
100
src/components/widgets/Background.jsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import * as Constants from '../../modules/constants';
|
||||
|
||||
export default class Background extends React.PureComponent {
|
||||
setBackground(url, colour, credit) { // Sets the attributes of the background image
|
||||
const background = colour ? `background-color: ${colour};` : `background-image: url(${url});`;
|
||||
|
||||
document.querySelector('#backgroundImage').setAttribute(
|
||||
'style',
|
||||
`${background};
|
||||
-webkit-filter: blur(${localStorage.getItem('blur')}px);
|
||||
-webkit-filter: brightness(${localStorage.getItem('brightness')}%);`
|
||||
);
|
||||
|
||||
if (credit === 'false') document.querySelector('#credits').style.display = 'none'; // Hide the credit
|
||||
}
|
||||
|
||||
setCredit(photographer) {
|
||||
document.querySelector('#photographer').append(` ${photographer}`); // Append credit
|
||||
document.getElementById('credit').textContent = photographer;
|
||||
}
|
||||
|
||||
doOffline() { // Handles setting the background if the user is offline
|
||||
const offlineImages = require('../../modules/offlineImages.json');
|
||||
const photographers = Object.keys(offlineImages); // Get all photographers from the keys in offlineImages.json
|
||||
const photographer = photographers[Math.floor(Math.random() * photographers.length)]; // Select a random photographer from the keys
|
||||
const randomImage = offlineImages[photographer].photo[
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
]; // Select a random image
|
||||
const url = `../offline-images/${randomImage}.jpeg`;
|
||||
|
||||
this.setBackground(url);
|
||||
this.setCredit(photographer);
|
||||
|
||||
document.querySelector('#backgroundCredits').style.display = 'none'; // Hide the location icon
|
||||
}
|
||||
|
||||
async determineMode() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') return this.doOffline();
|
||||
|
||||
const photoPack = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour');
|
||||
const customBackground = localStorage.getItem('customBackground');
|
||||
const favourited = JSON.parse(localStorage.getItem('favourite'));
|
||||
|
||||
if (favourited) {
|
||||
this.setBackground(favourited.url, null, 'true');
|
||||
this.setCredit(favourited.credit);
|
||||
document.getElementById('location').textContent = favourited.location;
|
||||
} else if (photoPack) {
|
||||
const randomPhoto = photoPack[Math.floor(Math.random() * photoPack.length)];
|
||||
this.setBackground(randomPhoto.url.default, null, randomPhoto.photographer);
|
||||
this.setCredit(randomPhoto.photographer);
|
||||
document.getElementById('location').textContent = randomPhoto.location;
|
||||
} else if (customBackgroundColour) {
|
||||
this.setBackground(null, customBackgroundColour, 'false');
|
||||
} else if (customBackground !== '') { // Local
|
||||
this.setBackground(customBackground, null, 'false');
|
||||
} else { // Online
|
||||
try { // First we try and get an image from the API...
|
||||
const enabled = localStorage.getItem('webp');
|
||||
let requestURL;
|
||||
|
||||
switch (localStorage.getItem('backgroundAPI')) {
|
||||
case 'unsplash':
|
||||
requestURL = `${Constants.UNSPLASH_URL}/getImage`;
|
||||
break;
|
||||
default: // Defaults to Mue
|
||||
if (localStorage.getItem('supportswebp') === 'true' && enabled === 'true') requestURL = `${Constants.API_URL}/getImage?webp=true`;
|
||||
else requestURL = `${Constants.API_URL}/getImage?category=Outdoors`;
|
||||
break;
|
||||
}
|
||||
|
||||
const data = await (await fetch(requestURL)).json(); // Fetch JSON data from requestURL
|
||||
|
||||
if (data.statusCode === 429) {
|
||||
this.doOffline(); // If we hit the rate limit, fallback to local images
|
||||
} else { // Otherwise, set the background and credit from remote data
|
||||
this.setBackground(data.file);
|
||||
this.setCredit(data.photographer);
|
||||
}
|
||||
|
||||
if (data.location.replace(/[null]+/g, '') === ' ') return document.querySelector('#backgroundCredits').style.display = 'none';
|
||||
document.getElementById('location').innerText = `${data.location.replace('null', '')}`; // Set the location tooltip
|
||||
} catch (e) { // ..and if that fails we load one locally
|
||||
this.doOffline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('background') === 'false') return document.querySelector('#credits').style.display = 'none'; // Hide the credit
|
||||
if (localStorage.getItem('animations') === 'true') document.querySelector('#backgroundImage').classList.add('fade-in');
|
||||
this.determineMode();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div id='backgroundImage'></div>;
|
||||
}
|
||||
}
|
||||
77
src/components/widgets/Clock.jsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import Analog from 'react-clock';
|
||||
|
||||
export default class Clock extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.timer = undefined;
|
||||
this.state = {
|
||||
time: '',
|
||||
ampm: ''
|
||||
};
|
||||
}
|
||||
|
||||
startTime(time = localStorage.getItem('seconds') === 'true' || localStorage.getItem('analog') === 'true' ? (1000 - Date.now() % 1000) : (60000 - Date.now() % 60000)) {
|
||||
this.timer = setTimeout(() => {
|
||||
const now = new Date();
|
||||
|
||||
// Analog clock
|
||||
if (localStorage.getItem('analog') === 'true') {
|
||||
this.setState({
|
||||
time: now
|
||||
});
|
||||
} else {
|
||||
let sec = '';
|
||||
|
||||
// Extra 0
|
||||
const zero = localStorage.getItem('zero');
|
||||
|
||||
if (localStorage.getItem('seconds') === 'true') {
|
||||
if (zero === 'false') sec = `:${now.getSeconds()}`;
|
||||
else sec = `:${('00' + now.getSeconds()).slice(-2)}`;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('24hour') === 'true') {
|
||||
let time = '';
|
||||
if (zero === 'false') time = `${now.getHours()}:${now.getMinutes()}${sec}`;
|
||||
|
||||
else time = `${('00' + now.getHours()).slice(-2)}:${('00' + now.getMinutes()).slice(-2)}${sec}`;
|
||||
this.setState({
|
||||
time: time
|
||||
});
|
||||
} else {
|
||||
// 12 hour support
|
||||
let hours = now.getHours();
|
||||
if (hours > 12) hours -= 12;
|
||||
|
||||
// Toggle AM/PM
|
||||
let ampm = now.getHours() > 11 ? 'PM' : 'AM';
|
||||
if (localStorage.getItem('ampm') === 'false') ampm = '';
|
||||
|
||||
let time = '';
|
||||
if (zero === 'false') time = `${hours}:${now.getMinutes()}${sec}`;
|
||||
else time = `${('00' + hours).slice(-2)}:${('00' + now.getMinutes()).slice(-2)}${sec}`;
|
||||
|
||||
this.setState({
|
||||
time: time,
|
||||
ampm: ampm
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.startTime();
|
||||
}, time);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('time') === 'false') return;
|
||||
this.startTime(0);
|
||||
}
|
||||
|
||||
render() {
|
||||
let clockHTML = <h1 className='clock'>{this.state.time}<span className='ampm'>{this.state.ampm}</span> </h1>;
|
||||
if (localStorage.getItem('analog') === 'true') clockHTML = <Analog className='analogclock' value={this.state.time} renderHourMarks={false} renderMinuteMarks={false} />;
|
||||
return clockHTML;
|
||||
}
|
||||
}
|
||||
36
src/components/widgets/Favourite.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import StarIcon from '@material-ui/icons/Star';
|
||||
import StarIcon2 from '@material-ui/icons/StarBorder';
|
||||
|
||||
export default class Favourite extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
favourited: <StarIcon2 id='favouriteButton' onClick={() => this.favourite()} />
|
||||
};
|
||||
}
|
||||
|
||||
favourite() {
|
||||
if (localStorage.getItem('favourite')) {
|
||||
localStorage.removeItem('favourite');
|
||||
this.setState({ favourited: <StarIcon2 id='favouriteButton' onClick={() => this.favourite()} /> });
|
||||
} else {
|
||||
const url = document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', '');
|
||||
const credit = document.getElementById('credit').textContent;
|
||||
const location = document.getElementById('location').textContent;
|
||||
localStorage.setItem('favourite', JSON.stringify({ url: url, credit: credit, location: location }));
|
||||
this.setState({ favourited: <StarIcon id='favouriteButton' onClick={() => this.favourite()} /> });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('favourite')) this.setState({ favourited: <StarIcon id='favouriteButton' onClick={() => this.favourite()} /> });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('favouriteEnabled') === 'false') return null;
|
||||
return <div className='favourite'>
|
||||
{this.state.favourited}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
64
src/components/widgets/Greeting.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
|
||||
export default class Greeting extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
greeting: ''
|
||||
};
|
||||
}
|
||||
|
||||
doEvents(time, message) {
|
||||
if (localStorage.getItem('events') === 'false') return message;
|
||||
|
||||
// Get current month & day
|
||||
const m = time.getMonth();
|
||||
const d = time.getDate();
|
||||
|
||||
if (m === 11 && d === 25) message = 'Merry Christmas'; // If it's December 25th, set the greeting string to "Merry Christmas"
|
||||
else if (m === 0 && d === 1) message = 'Happy new year'; // If the date is January 1st, set the greeting string to "Happy new year"
|
||||
else if (m === 9 && d === 31) message = 'Happy Halloween'; // If it's October 31st, set the greeting string to "Happy Halloween"
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
getGreeting() {
|
||||
const now = new Date();
|
||||
const hour = now.getHours();
|
||||
|
||||
let message = this.props.language.evening; // Set the default greeting string to "Good evening"
|
||||
if (hour < 12) message = this.props.language.morning; // If it's before 12am, set the greeting string to "Good morning"
|
||||
else if (hour < 18) message = this.props.language.afternoon; // If it's before 6pm, set the greeting string to "Good afternoon"
|
||||
|
||||
// Events
|
||||
message = this.doEvents(now, message);
|
||||
let custom = localStorage.getItem('defaultGreetingMessage');
|
||||
if (custom === 'false') message = '';
|
||||
|
||||
// Name
|
||||
let name = '';
|
||||
const data = localStorage.getItem('greetingName');
|
||||
|
||||
if (typeof data === 'string') {
|
||||
if (data.replace(/\s/g, '').length > 0) name = `, ${data.trim()}`;
|
||||
}
|
||||
|
||||
if (custom === 'false') name = name.replace(',', '');
|
||||
|
||||
// Set the state to the greeting string
|
||||
this.setState({
|
||||
greeting: `${message}${name}`
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('greeting') === 'false') return;
|
||||
this.getGreeting();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <h1 className='greeting'>
|
||||
{this.state.greeting}
|
||||
</h1>;
|
||||
}
|
||||
}
|
||||
45
src/components/widgets/Maximise.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import FullscreenIcon from '@material-ui/icons/Fullscreen';
|
||||
|
||||
export default class View extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
hidden: false
|
||||
};
|
||||
}
|
||||
|
||||
setAttribute(blur, brightness) {
|
||||
document.querySelector('#backgroundImage').setAttribute(
|
||||
'style',
|
||||
`background-image: url(${document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', '')});
|
||||
-webkit-filter: blur(${blur});
|
||||
-webkit-filter: brightness(${brightness}%);`
|
||||
);
|
||||
}
|
||||
|
||||
viewStuff() {
|
||||
const elements = ['#searchBar', '.navbar-container', '.clock', '.greeting', '.quotediv', 'time'];
|
||||
elements.forEach((element) => {
|
||||
try {
|
||||
(this.state.hidden === false) ? document.querySelector(element).style.display = 'none' : document.querySelector(element).style.display = 'block';
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (this.state.hidden === false) {
|
||||
this.setState({ hidden: true });
|
||||
this.setAttribute(0, 100);
|
||||
} else {
|
||||
this.setState({ hidden: false });
|
||||
this.setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (localStorage.getItem('view') === 'false') return null;
|
||||
return <div className='view'>
|
||||
<FullscreenIcon id='viewButton' onClick={() => this.viewStuff()} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
93
src/components/widgets/Quote.jsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import Quotes from '@muetab/quotes';
|
||||
import FileCopy from '@material-ui/icons/FilterNone';
|
||||
import { toast } from 'react-toastify';
|
||||
import * as Constants from '../../modules/constants';
|
||||
import TwitterIcon from '@material-ui/icons/Twitter';
|
||||
|
||||
export default class Quote extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
quote: '',
|
||||
author: ''
|
||||
};
|
||||
}
|
||||
|
||||
doOffline() {
|
||||
const quote = Quotes.random(); // Get a random quote from our local package
|
||||
this.setState({
|
||||
quote: '"' + quote.quote + '"',
|
||||
author: quote.author
|
||||
}); // Set the quote
|
||||
}
|
||||
|
||||
getQuotePack() {
|
||||
let quotePack = localStorage.getItem('quote_packs');
|
||||
if (quotePack === 'undefined') return this.doOffline();
|
||||
quotePack = JSON.parse(quotePack);
|
||||
|
||||
if (quotePack) {
|
||||
const data = quotePack[Math.floor(Math.random() * quotePack.length)];
|
||||
return this.setState({
|
||||
quote: '"' + data.quote + '"',
|
||||
author: data.author
|
||||
});
|
||||
} else this.doOffline();
|
||||
}
|
||||
|
||||
async getQuote() {
|
||||
const quotePackAPI = JSON.parse(localStorage.getItem('quote_api'));
|
||||
if (quotePackAPI) {
|
||||
try {
|
||||
const data = await (await fetch(quotePackAPI.url)).json();
|
||||
let author = data[quotePackAPI.author];
|
||||
if (quotePackAPI.authorOverride) author = quotePackAPI.authorOverride;
|
||||
return this.setState({
|
||||
quote: '"' + data[quotePackAPI.quote] + '"',
|
||||
author: author
|
||||
});
|
||||
} catch (e) {
|
||||
return this.getQuotePack();
|
||||
}
|
||||
}
|
||||
|
||||
if (localStorage.getItem('offlineMode') === 'true') return this.doOffline();
|
||||
|
||||
try { // First we try and get a quote from the API...
|
||||
const data = await (await fetch(Constants.API_URL + '/getQuote')).json();
|
||||
if (data.statusCode === 429) return this.doOffline(); // If we hit the ratelimit, we fallback to local quotes
|
||||
this.setState({
|
||||
quote: '"' + data.quote + '"',
|
||||
author: data.author
|
||||
});
|
||||
} catch (e) { // ..and if that fails we load one locally
|
||||
this.doOffline();
|
||||
}
|
||||
}
|
||||
|
||||
copyQuote() {
|
||||
navigator.clipboard.writeText(`${this.state.quote} - ${this.state.author}`);
|
||||
toast(this.props.language.quote);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('quote') === 'false') return;
|
||||
this.getQuote();
|
||||
}
|
||||
|
||||
render() {
|
||||
let copy = <FileCopy className='copyButton' onClick={() => this.copyQuote()}></FileCopy>;
|
||||
if (localStorage.getItem('copyButton') === 'false') copy = null;
|
||||
|
||||
let tweet = <TwitterIcon className='copyButton' onClick={() => window.open(`https://twitter.com/intent/tweet?text=${this.state.quote} - ${this.state.author} on @getmue`, '_blank').focus()}/>
|
||||
if (localStorage.getItem('tweetButton') === 'false') tweet = null;
|
||||
|
||||
return (
|
||||
<div className='quotediv'>
|
||||
<h1 className='quote'>{`${this.state.quote}`}</h1>
|
||||
<h1 className='quoteauthor'>{this.state.author} {copy} {tweet}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
38
src/components/widgets/Search.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
const searchEngines = require('../../modules/searchEngines.json');
|
||||
|
||||
export default class Search extends React.PureComponent {
|
||||
render() {
|
||||
if (localStorage.getItem('searchBar') === 'false') return null;
|
||||
|
||||
let url;
|
||||
let query = 'q';
|
||||
|
||||
const setting = localStorage.getItem('searchEngine');
|
||||
const info = searchEngines.find(i => i.settingsName === setting);
|
||||
if (info !== undefined) {
|
||||
url = info.url;
|
||||
if (info.query) query = info.query;
|
||||
}
|
||||
|
||||
const custom = localStorage.getItem('customSearchEngine');
|
||||
if (custom) url = custom;
|
||||
|
||||
const searchButton = () => {
|
||||
let value = document.getElementById('searchtext').value;
|
||||
if (!value) value = 'mue fast';
|
||||
window.location.href = url + `?${query}=` + value;
|
||||
};
|
||||
|
||||
return (
|
||||
<div id='searchBar' className='searchbar'>
|
||||
<form id='searchBar' className='searchbarform' action={url}>
|
||||
<SearchIcon onClick={() => searchButton()} />
|
||||
<input type='text' placeholder={this.props.language} name={query} id='searchtext' className='searchtext'/>
|
||||
<div className='blursearcbBG'/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
//* Imports
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
//* Render
|
||||
import './scss/index.scss';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<App/>,
|
||||
document.getElementById('root')
|
||||
|
||||
4
src/modules/constants.js
Normal file
@@ -0,0 +1,4 @@
|
||||
export const API_URL = 'https://api.muetab.xyz';
|
||||
export const UNSPLASH_URL = 'https://unsplash.muetab.xyz';
|
||||
export const MARKETPLACE_URL = 'https://marketplace.muetab.xyz';
|
||||
export const OFFLINE_IMAGES = 20;
|
||||
86
src/modules/defaultSettings.json
Normal file
@@ -0,0 +1,86 @@
|
||||
[
|
||||
{
|
||||
"name": "time",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "greeting",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "background",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "quote",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "searchBar",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "blur",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"name": "brightness",
|
||||
"value": 100
|
||||
},
|
||||
{
|
||||
"name": "events",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "customBackgroundColour",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "customBackground",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "greetingName",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "defaultGreetingMessage",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "backgroundAPI",
|
||||
"value": "mue"
|
||||
},
|
||||
{
|
||||
"name": "ampm",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "copyButton",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "installed",
|
||||
"value": "[]"
|
||||
},
|
||||
{
|
||||
"name": "searchEngine",
|
||||
"value": "duckduckgo"
|
||||
},
|
||||
{
|
||||
"name": "refresh",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "view",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "favouriteEnabled",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "tweetButton",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
44
src/modules/marketplaceFunctions.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default class MarketplaceFunctions {
|
||||
static urlParser (input) { // based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
|
||||
const urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/;
|
||||
return input.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
|
||||
}
|
||||
|
||||
static uninstall(name, type) {
|
||||
let installed = JSON.parse(localStorage.getItem('installed'));
|
||||
|
||||
const uninstallStuff = () => {
|
||||
for (let i = 0; i < installed.length; i++) {
|
||||
if (installed[i].name === name) {
|
||||
installed.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
localStorage.setItem('installed', JSON.stringify(installed));
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'settings':
|
||||
const oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
|
||||
localStorage.clear();
|
||||
oldSettings.forEach(item => localStorage.setItem(item.name, item.value));
|
||||
uninstallStuff();
|
||||
break;
|
||||
case 'quote_packs':
|
||||
localStorage.removeItem('quote_packs');
|
||||
localStorage.removeItem('quote_api');
|
||||
uninstallStuff();
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
localStorage.removeItem(type);
|
||||
uninstallStuff();
|
||||
} catch (e) {
|
||||
toast('Failed to uninstall addon, check the console');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/modules/merge.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Merges 2 objects into a huge one
|
||||
* @template T The original object
|
||||
* @template U The object that is returned
|
||||
* @param {...T} items The objects to merge
|
||||
* @returns {U} The merged object
|
||||
*/
|
||||
export const merge = (...items) => {
|
||||
const obj = {};
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
for (const k in items[i]) {
|
||||
if (!obj.hasOwnProperty(k)) obj[k] = items[i][k];
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
20
src/modules/offlineImages.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"Tirachard Kumtanom" : {
|
||||
"photo": [1]
|
||||
},
|
||||
"Sohail Na": {
|
||||
"photo": [4]
|
||||
},
|
||||
"Miriam Espacio": {
|
||||
"photo": [7]
|
||||
},
|
||||
"Fabian Wiktor": {
|
||||
"photo": [20]
|
||||
},
|
||||
"Pixabay": {
|
||||
"photo": [2, 3, 9, 11, 13, 14, 15]
|
||||
},
|
||||
"Unknown" : {
|
||||
"photo": [5, 6, 8, 10, 12, 16, 17, 18, 19]
|
||||
}
|
||||
}
|
||||
48
src/modules/searchEngines.json
Normal file
@@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"name": "DuckDuckGo",
|
||||
"settingsName": "duckduckgo",
|
||||
"url": "https://duckduckgo.com"
|
||||
},
|
||||
{
|
||||
"name": "Google",
|
||||
"settingsName": "google",
|
||||
"url": "https://google.com/search"
|
||||
},
|
||||
{
|
||||
"name": "Bing",
|
||||
"settingsName": "bing",
|
||||
"url": "https://bing.com/search"
|
||||
},
|
||||
{
|
||||
"name": "Yahoo",
|
||||
"settingsName": "yahoo",
|
||||
"url": "https://search.yahoo.com/search"
|
||||
},
|
||||
{
|
||||
"name": "Ecosia",
|
||||
"settingsName": "ecosia",
|
||||
"url": "https://ecosia.org/search"
|
||||
},
|
||||
{
|
||||
"name": "Яндекс",
|
||||
"settingsName": "yandex",
|
||||
"url": "https://yandex.ru/search",
|
||||
"query": "text"
|
||||
},
|
||||
{
|
||||
"name": "Qwant",
|
||||
"settingsName": "qwant",
|
||||
"url": "https://www.quant.com/"
|
||||
},
|
||||
{
|
||||
"name": "Ask",
|
||||
"settingsName": "ask",
|
||||
"url": "https://ask.com/web"
|
||||
},
|
||||
{
|
||||
"name": "Startpage",
|
||||
"settingsName": "startpage",
|
||||
"url": "https://www.startpage.com/sp/search"
|
||||
}
|
||||
]
|
||||
99
src/modules/settingsFunctions.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import detectBrowserLanguage from 'detect-browser-language';
|
||||
|
||||
const saveFile = (data, filename = 'file') => {
|
||||
if (!data) return console.error('No data');
|
||||
if (typeof data === 'object') data = JSON.stringify(data, undefined, 4);
|
||||
|
||||
const blob = new Blob([data], { type: 'text/json' });
|
||||
let e = document.createEvent('MouseEvents');
|
||||
let a = document.createElement('a');
|
||||
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = filename;
|
||||
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
|
||||
|
||||
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
a.dispatchEvent(e);
|
||||
};
|
||||
|
||||
const defaultSettings = require('./defaultSettings.json');
|
||||
|
||||
export default class SettingsFunctions {
|
||||
static exportSettings() {
|
||||
let settings = {};
|
||||
for (const key of Object.keys(localStorage)) settings[key] = localStorage.getItem(key);
|
||||
saveFile(settings, 'mue-settings.json');
|
||||
}
|
||||
|
||||
static setItem(key, value) {
|
||||
const old = localStorage.getItem(key);
|
||||
let val = true;
|
||||
|
||||
if (old !== null && !value) {
|
||||
if (old === 'true') val = false;
|
||||
if (old === 'false') val = true;
|
||||
}
|
||||
|
||||
localStorage.setItem(key, val);
|
||||
}
|
||||
|
||||
static toggleExtra(element, element2) {
|
||||
(element.style.display === 'none' || !element.style.display) ? element.style.display = 'block' : element.style.display = 'none';
|
||||
(element2.style.transform === 'rotate(-180deg)') ? element2.style.transform = 'rotate(0)' : element2.style.transform = 'rotate(-180deg)';
|
||||
}
|
||||
|
||||
static setSearchEngine(input) {
|
||||
const searchEngineInput = document.getElementById('searchEngineInput');
|
||||
if (input === 'custom') {
|
||||
searchEngineInput.enabled = 'true';
|
||||
searchEngineInput.style.display = 'block';
|
||||
} else {
|
||||
searchEngineInput.style.display = 'none';
|
||||
searchEngineInput.enabled = 'false';
|
||||
localStorage.setItem('searchEngine', input);
|
||||
}
|
||||
}
|
||||
|
||||
static saveStuff() {
|
||||
localStorage.setItem('blur', document.getElementById('blurRange').value); // this is better than inline onChange for performance
|
||||
localStorage.setItem('brightness', document.getElementById('brightnessRange').value);
|
||||
localStorage.setItem('greetingName', document.getElementById('greetingName').value);
|
||||
localStorage.setItem('customBackground', document.getElementById('customBackground').value);
|
||||
if (document.getElementById('customBackgroundHex').textContent !== 'Disabled') {
|
||||
localStorage.setItem('customBackgroundColour', document.getElementById('customBackgroundHex').textContent);
|
||||
}
|
||||
if (document.getElementById('searchEngineInput').enabled === 'true') {
|
||||
const input = document.getElementById('customSearchEngine').value;
|
||||
if (input) {
|
||||
localStorage.setItem('searchEngine', 'custom');
|
||||
localStorage.setItem('customSearchEngine', input);
|
||||
}
|
||||
}
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
static setDefaultSettings() {
|
||||
localStorage.clear();
|
||||
defaultSettings.forEach((element) => localStorage.setItem(element.name, element.value));
|
||||
|
||||
// Set theme depending on user preferred
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) localStorage.setItem('darkTheme', true);
|
||||
else localStorage.setItem('darkTheme', false);
|
||||
|
||||
// Webp support
|
||||
const supportsWebP = require('supports-webp'); // We import it here so it doesn't run the function on each page load
|
||||
if (supportsWebP) localStorage.setItem('supportswebp', 'true');
|
||||
|
||||
// Languages
|
||||
const languages = ['nl', 'no', 'fr', 'ru'];
|
||||
const browserLanguage = detectBrowserLanguage();
|
||||
if (languages.includes(browserLanguage)) {
|
||||
localStorage.setItem('language', browserLanguage);
|
||||
document.documentElement.lang = browserLanguage;
|
||||
} else localStorage.setItem('language', 'en');
|
||||
|
||||
// Finally we set this to true so it doesn't run the function on every load
|
||||
localStorage.setItem('firstRun', true);
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
28
src/scss/_mixins.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
// credit: https://joshbroton.com/quick-fix-sass-mixins-for-css-keyframe-animations/
|
||||
@mixin animation($animate...) {
|
||||
$max: length($animate);
|
||||
$animations: '';
|
||||
|
||||
@for $i from 1 through $max {
|
||||
$animations: #{$animations + nth($animate, $i)};
|
||||
|
||||
@if $i < $max {
|
||||
$animations: #{$animations + ', '};
|
||||
}
|
||||
}
|
||||
-webkit-animation: $animations;
|
||||
-moz-animation: $animations;
|
||||
animation: $animations;
|
||||
}
|
||||
|
||||
@mixin keyframes($animationName) {
|
||||
@-webkit-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@-moz-keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
@keyframes #{$animationName} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
31
src/scss/_variables.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
@use 'sass:map';
|
||||
|
||||
$theme-colours: (
|
||||
'gradient': linear-gradient(90deg, #ffb032 0%, #dd3b67 100%),
|
||||
'main': rgba(242, 243, 244, 1),
|
||||
'secondary': rgba(0, 0, 0, 1),
|
||||
'main-text-color': rgba(242, 243, 244, 1),
|
||||
);
|
||||
|
||||
$modal: (
|
||||
'background': #fff,
|
||||
'text': rgba(0, 0, 0, 1),
|
||||
'tab-underline': rgba(204, 204, 204, 1),
|
||||
'tab-underline-active': rgba(0, 0, 0, 1),
|
||||
'border-radius': 12px,
|
||||
);
|
||||
|
||||
$marketplace: (
|
||||
'item-background': rgba(242, 243, 244, 1),
|
||||
'product-information-backgroud': rgba(242, 243, 244, 1),
|
||||
);
|
||||
|
||||
$button-colours: (
|
||||
'confirm': rgba(46, 213, 115, 1),
|
||||
'reset': rgba(255, 71, 87, 1),
|
||||
'other': rgba(83, 82, 237, 1),
|
||||
);
|
||||
|
||||
$main-parts: (
|
||||
'shadow': 0 0 1rem 0 rgba(0, 0, 0, .2),
|
||||
);
|
||||
@@ -1,8 +1,94 @@
|
||||
/* Imports */
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
@import 'modules/clock';
|
||||
@import 'modules/greeting';
|
||||
@import 'modules/quote';
|
||||
@import 'modules/search';
|
||||
@import 'modules/credit';
|
||||
/*@import 'modules/navbar';*/
|
||||
@import 'modules/miscellaneous';
|
||||
@import 'modules/navbar';
|
||||
@import 'modules/modal';
|
||||
@import 'modules/settings';
|
||||
@import 'modules/toast';
|
||||
@import 'modules/marketplace';
|
||||
@import 'modules/checkbox';
|
||||
@import 'modules/buttons';
|
||||
@import 'modules/welcome';
|
||||
|
||||
body {
|
||||
background: #2f3640;
|
||||
margin: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: 'Lexend Deca', sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Lexend Deca', 'Roboto' !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lexend Deca';
|
||||
src: url('/./fonts/LexendDeca-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url('/./fonts/Roboto-Cyrillic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
#center {
|
||||
margin-left: 2vw;
|
||||
margin-right: 2vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: map-get($theme-colours, 'main');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
color: map-get($theme-colours, 'main-text-color');
|
||||
}
|
||||
|
||||
#backgroundImage {
|
||||
height: 100vh;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
@include animation('fadein 2s');
|
||||
}
|
||||
|
||||
@include keyframes(fadein) {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.backgroundEffects {
|
||||
opacity: 0.7;
|
||||
transition: ease 0.6s;
|
||||
}
|
||||
|
||||
#searchEngine {
|
||||
width: 130px;
|
||||
}
|
||||
160
src/scss/modules/_buttons.scss
Normal file
@@ -0,0 +1,160 @@
|
||||
%settingsButton {
|
||||
text-align: center;
|
||||
border: none;
|
||||
transition: ease 0.33s;
|
||||
color: map-get($theme-colours, "main");
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 10px 30px;
|
||||
font-size: 20px;
|
||||
border-radius: 24px;
|
||||
background: none;
|
||||
&:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.apply {
|
||||
@extend %settingsButton;
|
||||
margin-right: 20px;
|
||||
background-color: map-get($button-colours, "confirm");
|
||||
border: 2px solid map-get($button-colours, "confirm");
|
||||
|
||||
&:hover {
|
||||
border: 2px solid map-get($button-colours, "confirm");
|
||||
color: map-get($button-colours, "confirm");
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.reset {
|
||||
@extend %settingsButton;
|
||||
background-color: map-get($button-colours, "reset");
|
||||
border: 2px solid map-get($button-colours, "reset");
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid map-get($button-colours, "reset");
|
||||
color: map-get($button-colours, "reset");
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.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");
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.export,
|
||||
.uploadbg,
|
||||
.import {
|
||||
@extend %settingsButton;
|
||||
background-color: map-get($button-colours, "other");
|
||||
border: 2px solid map-get($button-colours, "other");
|
||||
color: map-get($theme-colours, "main");
|
||||
|
||||
&:hover {
|
||||
color: map-get($button-colours, "other");
|
||||
border: 2px solid map-get($button-colours, "other");
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.export,
|
||||
.import {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.storebutton {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
float: right;
|
||||
vertical-align: middle;
|
||||
padding: 5px 30px;
|
||||
background: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
border: 2px solid #2d3436;
|
||||
border-radius: 24px;
|
||||
font-family: 'Lexend Deca', sans-serif;
|
||||
transition: ease 0.33s;
|
||||
|
||||
&:hover {
|
||||
background: #2d3436;
|
||||
color: map-get($theme-colours, "main");;
|
||||
border: 2px solid #2d3436;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .storebutton {
|
||||
border: 2px solid map-get($theme-colours, "main");
|
||||
color: map-get($theme-colours, "main");
|
||||
|
||||
&:hover {
|
||||
background: map-get($theme-colours, "main");
|
||||
color: #2d3436;
|
||||
border: 2px solid map-get($theme-colours, "main");
|
||||
}
|
||||
}
|
||||
|
||||
#item >.removeFromMue {
|
||||
border: 2px solid #ff4757;
|
||||
color: #ff4757;
|
||||
|
||||
&:hover {
|
||||
background: #ff4757;
|
||||
color: map-get($theme-colours, "main");;
|
||||
border: 2px solid #ff4757;
|
||||
}
|
||||
|
||||
@extend .storebutton;
|
||||
}
|
||||
|
||||
#item .addToMue,
|
||||
#item .removeFromMue {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.addToMue {
|
||||
@extend .storebutton;
|
||||
}
|
||||
|
||||
.goToMarket {
|
||||
float: none;
|
||||
@extend .storebutton;
|
||||
}
|
||||
|
||||
.sideload {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.seemore {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.seemore {
|
||||
@extend %settingsButton;
|
||||
background-color: #2d3436;
|
||||
border: 2px solid #2d3436;
|
||||
|
||||
&:hover {
|
||||
color: #2d3436;
|
||||
border: 2px solid #2d3436;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
3
src/scss/modules/_checkbox.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.MuiCheckbox-colorPrimary.Mui-checked {
|
||||
color: map-get($button-colours, "reset") !important;
|
||||
}
|
||||
@@ -1,9 +1,20 @@
|
||||
.clock {
|
||||
font-size: 4em;
|
||||
margin: 0;
|
||||
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.ampm {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
.analogclock, .react-clock__face {
|
||||
margin: 0 auto;
|
||||
border-radius: 100%;
|
||||
box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid map-get($theme-colours, "main");
|
||||
}
|
||||
|
||||
.react-clock__hand__body {
|
||||
background: map-get($theme-colours, "main");;
|
||||
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -9,6 +9,18 @@
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.view {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.favourite {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 50px;
|
||||
}
|
||||
|
||||
#photographer {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
@@ -16,19 +28,17 @@
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.locationicon {
|
||||
font-size: calc(10px + 1.2vmin);
|
||||
.locationicon, #viewButton, #favouriteButton {
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.MuiSvgIcon-root {
|
||||
.navbar-container > svg, #backgroundCredits > svg, #viewButton > svg, #favouriteButton > svg {
|
||||
font-size: calc(10px + 1.5vmin) !important;
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
left: 2px;
|
||||
width: 100em;
|
||||
height: 100em;
|
||||
font-size: 2rem;
|
||||
text-shadow: 0 2px 25px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.5s ease 0s;
|
||||
}
|
||||
|
||||
.credits {
|
||||
@@ -36,34 +46,44 @@
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
min-width: 50px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border-bottom: 1px dotted black;
|
||||
bottom: 15px;
|
||||
left: 10px;
|
||||
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4));
|
||||
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4));
|
||||
|
||||
.tooltiptext {
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
background-color: black;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
background-color: map-get($theme-colours, "main");;
|
||||
color: #000;
|
||||
font-size: calc(10px + 1.2vmin);
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
padding: 15px 32px;
|
||||
bottom: 40px;
|
||||
left: 60px;
|
||||
margin-left: -60px;
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
#photographer, .locationicon {
|
||||
display: inline;
|
||||
vertical-align: midddle;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
.greeting {
|
||||
margin: 0;
|
||||
font-size: 1.6em;
|
||||
text-shadow: 0 0 25px rgba(0,0,0,0.3);
|
||||
}
|
||||
286
src/scss/modules/_marketplace.scss
Normal file
@@ -0,0 +1,286 @@
|
||||
.tab,
|
||||
button.tablinks {
|
||||
margin-top: -10px;
|
||||
font-size: 24px;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: map-get($modal, "text");
|
||||
}
|
||||
|
||||
button.tablinks {
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
border-radius: 12px;
|
||||
padding: 10px 30px 10px 30px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(189, 195, 199, .075);
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
padding-top: 10px;
|
||||
border-bottom: 3px solid map-get($modal, "tab-underline");
|
||||
}
|
||||
}
|
||||
|
||||
#item a {
|
||||
color: map-get($button-colours, "other");
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#active:after {
|
||||
content: "";
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
padding-top: 10px;
|
||||
border-bottom: 3px solid map-get($modal, "tab-underline-active");
|
||||
}
|
||||
|
||||
.dark {
|
||||
.tab,
|
||||
button.tablinks {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#active:after {
|
||||
border-image-slice: 1;
|
||||
border-image-source: map-get($theme-colours, "gradient");
|
||||
border-bottom: 3px solid;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
align-items: left;
|
||||
justify-content: left;
|
||||
margin-top: -10px;
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
height: 80px;
|
||||
width: 270px;
|
||||
background: map-get($marketplace, "item-background");
|
||||
transition: 0.5s;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: none;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
border-radius: 12px 0 0 12px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
img,
|
||||
.details {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.details {
|
||||
position: absolute;
|
||||
left: 90px;
|
||||
top: 15px;
|
||||
|
||||
img {
|
||||
margin-left: 10px;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
p.desc {
|
||||
margin-top: -14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark .items .item {
|
||||
background: #2d3436;
|
||||
}
|
||||
|
||||
p.author {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
#item {
|
||||
display: none;
|
||||
|
||||
h1 {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
#item > h1,
|
||||
#item > .MuiSvgIcon-root {
|
||||
display: inline;
|
||||
font-size: 35px !important;
|
||||
}
|
||||
|
||||
|
||||
p.description {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.emptyMessage {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
transform: translateY(30%);
|
||||
|
||||
svg {
|
||||
font-size: 50px;
|
||||
line-height: 0px;
|
||||
margin: 0;
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.backArrow {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
|
||||
.informationContainer {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.productInformation {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: map-get($marketplace, "product-information-backgroud");
|
||||
width: 200px;
|
||||
border-radius: 12px;
|
||||
|
||||
li {
|
||||
margin-left: -4px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li.header {
|
||||
text-transform: uppercase;
|
||||
color: #787878;
|
||||
list-style: none;
|
||||
margin-left: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .productInformation {
|
||||
background: #2d3436;
|
||||
}
|
||||
|
||||
#item > img {
|
||||
border-radius: 24px;
|
||||
height: 200px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#marketplace {
|
||||
-webkit-animation: content 0.5s;
|
||||
-moz-animation: content 0.5s;
|
||||
-ms-animation: content 0.5s;
|
||||
-o-animation: content 0.5s;
|
||||
animation: content 0.5s;
|
||||
}
|
||||
|
||||
@keyframes content {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(5%);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0%);
|
||||
}
|
||||
}
|
||||
|
||||
.banner {
|
||||
text-align: center;
|
||||
background: #54a0ff;
|
||||
border-radius: 24px;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
color: map-get($theme-colours, "main");
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.featured {
|
||||
background: #2d3436;
|
||||
margin-top: 20px;
|
||||
border-radius: 24px;
|
||||
padding: 50px;
|
||||
color: map-get($theme-colours, "main");
|
||||
|
||||
button {
|
||||
float: left;
|
||||
margin-top: -20px;
|
||||
border: 2px solid map-get($theme-colours, "main");
|
||||
color: map-get($theme-colours, "main");
|
||||
|
||||
&:hover {
|
||||
border: 2px solid map-get($theme-colours, "main");
|
||||
background: map-get($theme-colours, "main");
|
||||
color: #2d3436;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .featured {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.requires {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#seemore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#seemore svg {
|
||||
font-size: 35px !important;
|
||||
margin-bottom: -30px;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
body {
|
||||
background: #2f3640;
|
||||
margin: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: 'Lexend Deca';
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#center {
|
||||
margin-left: 2vw;
|
||||
margin-right: 2vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #ffffff;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#root {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lexend Deca';
|
||||
src: url('/./fonts/LexendDeca-Regular.woff2') format('woff2');
|
||||
}
|
||||
199
src/scss/modules/_modal.scss
Normal file
@@ -0,0 +1,199 @@
|
||||
.Modal {
|
||||
color: map-get($modal, "text");
|
||||
background-color: map-get($modal, "background");
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
|
||||
border: none;
|
||||
opacity: 1;
|
||||
z-index: -2;
|
||||
padding: 25px;
|
||||
cursor: hand;
|
||||
transition: 0.6s;
|
||||
transition-timing-function: ease-in;
|
||||
border-radius: map-get($modal, "border-radius");
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
scrollbar-color: #34495e #bdc3c7;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modalLink {
|
||||
color: #5352ed;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
float: right;
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: #2f3542 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.ReactModal__Html--open,
|
||||
.ReactModal__Body--open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@-webkit-keyframes zoom-in {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05, 1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom-in {
|
||||
0% {
|
||||
-webkit-transform: scale(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(1.05, 1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.Overlay {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
height: 90vh;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.modal-animation {
|
||||
-webkit-animation: zoom-in 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
animation: zoom-in 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 700px) {
|
||||
.ReactModal__Content {
|
||||
min-height: 500px;
|
||||
max-height: calc(100vh - 30vh);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-height: 700px) {
|
||||
.ReactModal__Content {
|
||||
min-height: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: -20px;
|
||||
width: 900px;
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.columns {
|
||||
font-size: 15px;
|
||||
|
||||
li {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-out {
|
||||
-webkit-animation: zoom-out 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
animation: zoom-out 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes zoom-out {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05, 1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom-out {
|
||||
0% {
|
||||
-webkit-transform: scale(1, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale(1.05, 1.05);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
.updateContent {
|
||||
width: 400px;
|
||||
padding: 5px;
|
||||
.closeModal {
|
||||
margin-top: 10px;
|
||||
font-size: 45px;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 13px;
|
||||
background: #bdc3c7;
|
||||
border-top-right-radius: map-get($modal, "border-radius");
|
||||
border-bottom-right-radius: map-get($modal, "border-radius");
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #34495e;
|
||||
border-top-right-radius: map-get($modal, "border-radius");
|
||||
border-bottom-right-radius: map-get($modal, "border-radius");
|
||||
}
|
||||
@@ -2,24 +2,42 @@
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
min-width: 50px;
|
||||
cursor: pointer;
|
||||
-webkit-filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.3));
|
||||
filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.3));
|
||||
top: 20px;
|
||||
|
||||
svg {
|
||||
transition: ease 0.2s;
|
||||
font-size: calc(10px + 1.5vmin) !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
transform: scale(1.1);
|
||||
color: map-get($theme-colours, "main");;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar1 {
|
||||
@extend navbar;
|
||||
top: 50px;
|
||||
right: 0px;
|
||||
@extend %navbar;
|
||||
right: 20px;
|
||||
|
||||
svg {
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform: translateZ(0) scale(1, 1);
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0) scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.navbar2 {
|
||||
@extend navbar;
|
||||
top: 50px;
|
||||
right: 50px;
|
||||
@extend %navbar;
|
||||
right: 60px;
|
||||
}
|
||||
|
||||
.PizzaIcon {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.pizza {
|
||||
right: 2000px;
|
||||
.navbar3 {
|
||||
@extend %navbar;
|
||||
right: 100px;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
.quote {
|
||||
font-size: 0.8em;
|
||||
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
@@ -14,10 +14,43 @@
|
||||
font-size: 0.9em;
|
||||
letter-spacing: 0.5px;
|
||||
margin: 0;
|
||||
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.quoteAuthor, .copyButton {
|
||||
display: inline;
|
||||
font-size: 0.8em;
|
||||
position: relative !important;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.copyButton {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
float: middle;
|
||||
margin: 0 auto;
|
||||
text-align: right;
|
||||
transition: ease 0.2s !important;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
i.material-icons,
|
||||
h1.quoteauthor {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
button.copyButton {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: map-get($theme-colours, "main");;
|
||||
padding: 20px 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
display: table-cell
|
||||
}
|
||||
@@ -1,31 +1,39 @@
|
||||
.searchbar {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: block;
|
||||
color: #ffff;
|
||||
font-family: 'Lexend Deca';
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: block;
|
||||
color: map-get($theme-colours, "main-text-color");
|
||||
|
||||
input[type=text] {
|
||||
font-size: calc(5px + 1.2vmin);
|
||||
background: none;
|
||||
border: 2px solid #ffff;
|
||||
padding: 10px;
|
||||
color: #ffff;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
|
||||
z-index: 1;
|
||||
input[type=text] {
|
||||
width: 140px;
|
||||
margin-left: 12px;
|
||||
border-radius: 24px;
|
||||
font-size: calc(5px + 1.2vmin);
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: map-get($theme-colours, "main-text-color");
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
-webkit-transition: width 0.5s ease-in-out;
|
||||
transition: width 0.5s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
width: 400px;
|
||||
background-color: rgba(0, 0, 0, .3);
|
||||
}
|
||||
}
|
||||
|
||||
.MuiSvgIcon-root {
|
||||
margin-top: 4px;
|
||||
font-size: 30px;
|
||||
filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.3));
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.input.searchtext {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.searchbarform {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .25);
|
||||
}
|
||||
259
src/scss/modules/_settings.scss
Normal file
@@ -0,0 +1,259 @@
|
||||
.switch {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
float: right;
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
border-radius: 34px;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: #fff;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
&[type=text] {
|
||||
width: 200px;
|
||||
padding: 0.5rem 1rem;
|
||||
box-sizing: border-box;
|
||||
border-image-slice: 1;
|
||||
border-image-source: map-get($theme-colours, "gradient");
|
||||
outline: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:checked+.slider {
|
||||
background: map-get($theme-colours, "gradient");
|
||||
|
||||
&:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus+.slider {
|
||||
box-shadow: 0 0 1px #e67e22;
|
||||
}
|
||||
}
|
||||
|
||||
h4,
|
||||
.switch,
|
||||
.expandIcons {
|
||||
display: inline;
|
||||
font-size: 1.4em;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
h4,
|
||||
#engines {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.expandIcons {
|
||||
position: relative;
|
||||
font-size: 25px;
|
||||
vertical-align: middle;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
h4,
|
||||
.expandIcons {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.extraSettings {
|
||||
display: none;
|
||||
border-left: 10px solid;
|
||||
border-image-slice: 1;
|
||||
border-width: 5px;
|
||||
border-image-source: linear-gradient(to bottom, #ffb032 0%, #dd3b67 100%);
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=range],
|
||||
p,
|
||||
button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 5px;
|
||||
margin: 0;
|
||||
|
||||
>label {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.range {
|
||||
-webkit-appearance: none;
|
||||
width: 200px;
|
||||
height: 15px;
|
||||
background: #ccc;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
background: #ecf0f1;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 0 100px rgba(0, 0, 0, 0.3);
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 12px;
|
||||
background: map-get($theme-colours, "gradient");
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 12px;
|
||||
border: 0;
|
||||
background: map-get($theme-colours, "gradient");
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=color] {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
box-shadow: map-get($main-parts, "shadow");
|
||||
border: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
vertical-align: middle;
|
||||
|
||||
&::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// This is duplicated because it didn't work with a comma, need to look into it later
|
||||
input[type=color]::-moz-color-swatch {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
box-shadow: map-get($main-parts, "shadow");
|
||||
border: none;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
vertical-align: middle;
|
||||
|
||||
&::-moz-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&::-moz-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#customBackgroundHex {
|
||||
font-size: 1.2em;
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
select {
|
||||
background: none;
|
||||
height: 34px;
|
||||
width: 120px;
|
||||
border-image-slice: 1;
|
||||
border-image-source: linear-gradient(180deg, #ffb032 0%, #dd3b67 100%);
|
||||
border-left: 10px solid;
|
||||
color: map-get($modal, "text");
|
||||
}
|
||||
|
||||
select#language {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#customBackgroundColour {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
// Dark Theme
|
||||
.dark {
|
||||
#blurRange, #brightnessRange {
|
||||
background-color: #535c68;
|
||||
}
|
||||
|
||||
#customBackground, #backgroundAPI, #searchEngine, #language, #greetingName, #customSearchEngine {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.choices {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.modalLink {
|
||||
color: #3498db;
|
||||
}
|
||||
}
|
||||
|
||||
.nodropdown {
|
||||
cursor: default;
|
||||
}
|
||||
28
src/scss/modules/_toast.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
.Toastify__toast-body {
|
||||
color: #000 !important;
|
||||
font-size: 16px !important;
|
||||
padding: 15px 20px !important;
|
||||
}
|
||||
|
||||
.Toastify__toast {
|
||||
border-radius: 12px !important;
|
||||
min-height: auto !important;
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
min-width: auto !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.Toastify__close-button {
|
||||
margin-top: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.Toastify__progress-bar--default {
|
||||
height: 3px !important;
|
||||
background: #000 !important;
|
||||
}
|
||||
|
||||
.Toastify__toast-container {
|
||||
width: auto !important;
|
||||
}
|
||||
48
src/scss/modules/_welcome.scss
Normal file
@@ -0,0 +1,48 @@
|
||||
.welcomeModalText {
|
||||
line-height: 2px;
|
||||
|
||||
h2.subtitle {
|
||||
font-size: 24px;
|
||||
color: #535353;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1.welcometitle {
|
||||
font-size: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcomeContent {
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
line-height: 20px !important;
|
||||
color: #5352ED;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
img.icon, svg {
|
||||
margin-top: -12px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
transition: ease 0.2s;
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.7rem;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
img, svg {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
129
src/translations/en.json
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Good Morning",
|
||||
"afternoon": "Good Afternoon",
|
||||
"evening": "Good Evening",
|
||||
"christmas": "Merry Christmas",
|
||||
"newyear": "Happy new year",
|
||||
"halloween": "Happy Halloween"
|
||||
},
|
||||
"credit": "Photo by",
|
||||
"search": "Search",
|
||||
"modals": {
|
||||
"title": "Options",
|
||||
"settings": "Settings",
|
||||
"addons": "My Add-ons",
|
||||
"marketplace": "Marketplace"
|
||||
},
|
||||
"settings": {
|
||||
"time": {
|
||||
"title": "Time",
|
||||
"seconds": "Seconds",
|
||||
"twentyfourhour": "24 Hour",
|
||||
"ampm": "AM/PM (12 hour)",
|
||||
"zero": "Zero-padded",
|
||||
"analog": "Analog"
|
||||
},
|
||||
"greeting": {
|
||||
"title": "Greeting",
|
||||
"events": "Events",
|
||||
"default": "Default Greeting Message",
|
||||
"name": "Name for greeting"
|
||||
},
|
||||
"quote": {
|
||||
"title": "Quote",
|
||||
"copy": "Copy Button",
|
||||
"tweet": "Tweet Button"
|
||||
},
|
||||
"background": {
|
||||
"title": "Background",
|
||||
"API": "Background API",
|
||||
"blur": "Adjust Blur",
|
||||
"brightness": "Adjust Brightness",
|
||||
"customURL": "Custom Background URL",
|
||||
"custombackground": "Custom Background",
|
||||
"customcolour": "Custom Background Colour",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"searchbar": {
|
||||
"title": "Search Bar",
|
||||
"searchengine": "Search Engine",
|
||||
"custom": "Custom Search URL"
|
||||
},
|
||||
"offline": "Offline Mode",
|
||||
"experimental": {
|
||||
"title": "Experimental",
|
||||
"webp": "Enable WebP",
|
||||
"dark": "Dark Theme",
|
||||
"animations": "Animations",
|
||||
"view": "View",
|
||||
"favourite": "Favourite",
|
||||
"refresh": "Refresh"
|
||||
},
|
||||
"language": "Language",
|
||||
"apply": "Apply",
|
||||
"reset": "Reset",
|
||||
"import": "Import",
|
||||
"export": "Export",
|
||||
"imported_settings": "Imported settings"
|
||||
},
|
||||
"update": {
|
||||
"title": "Update",
|
||||
"offline": {
|
||||
"title": "Offline",
|
||||
"description": "Cannot get update logs while in offline mode"
|
||||
},
|
||||
"error": {
|
||||
"title": "Error",
|
||||
"content": "Could not connect to the server"
|
||||
},
|
||||
"loading": "Loading..."
|
||||
},
|
||||
"toasts": {
|
||||
"quote": "Quote Copied",
|
||||
"reset": "Reset successfully",
|
||||
"installed": "Successfully installed",
|
||||
"removed": "Successfully removed",
|
||||
"error": "Something went wrong",
|
||||
"imported": "Successfully imported"
|
||||
},
|
||||
"marketplace": {
|
||||
"photo_packs": "Photo Packs",
|
||||
"quote_packs": "Quote Packs",
|
||||
"preset_settings": "Preset Settings",
|
||||
"themes": "Themes",
|
||||
"product": {
|
||||
"overview": "Overview",
|
||||
"information": "Information",
|
||||
"last_updated": "Last Updated",
|
||||
"version": "Version",
|
||||
"author": "Author",
|
||||
"notice": {
|
||||
"title": "Notice",
|
||||
"description": "In order for the change to take place, the page must be refreshed."
|
||||
},
|
||||
"buttons": {
|
||||
"addtomue": "Add To Mue",
|
||||
"remove": "Remove"
|
||||
},
|
||||
"quoteWarning": {
|
||||
"title": "Warning",
|
||||
"description": "This quote pack requests to external servers that may track you!"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
"title": "Looks like you're offline",
|
||||
"description": "Please connect to the internet."
|
||||
},
|
||||
"see_more": "See More"
|
||||
},
|
||||
"addons": {
|
||||
"added": "Added",
|
||||
"empty": {
|
||||
"title": "It's empty here",
|
||||
"description": "Head to the marketplace to add some.",
|
||||
"button": "Take me there"
|
||||
},
|
||||
"sideload": "Sideload"
|
||||
}
|
||||
}
|
||||
115
src/translations/fr.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Bonjour",
|
||||
"afternoon": "Bonne après-midi",
|
||||
"evening": "Bonsoir",
|
||||
"christmas": "Bonne Noël",
|
||||
"newyear": "Bonne année",
|
||||
"halloween": "Joyeux Halloween"
|
||||
},
|
||||
"credit": "Photo par",
|
||||
"search": "Trouver",
|
||||
"modals": {
|
||||
"title": "Options",
|
||||
"settings": "Parametres",
|
||||
"addons": "Mes Options",
|
||||
"marketplace": "Marché"
|
||||
},
|
||||
"settings": {
|
||||
"time": {
|
||||
"title": "Temps",
|
||||
"seconds": "Seconds",
|
||||
"twentyfourhour": "24 Heures",
|
||||
"ampm": "AM/PM (12 hour)",
|
||||
"zero": "Zero-padded",
|
||||
"analog": "Analogue"
|
||||
},
|
||||
"greeting": {
|
||||
"title": "Salutation",
|
||||
"events": "Événements",
|
||||
"default": "Défaut Salutation",
|
||||
"name": "Nom Pour Salutation"
|
||||
},
|
||||
"quote": {
|
||||
"title": "Citation",
|
||||
"copy": "Au bouton copie"
|
||||
},
|
||||
"background": {
|
||||
"title": "Fond",
|
||||
"API": "Fond API",
|
||||
"blur": "Ajuster le flou",
|
||||
"customURL": "URL D'arrière-plan personnalisée",
|
||||
"custombackground": "D'arrière-plan personnalisée",
|
||||
"customcolour": "Couleur d'arrière-plan personnalisée"
|
||||
},
|
||||
"searchbar": {
|
||||
"title": "Barre de Recherche",
|
||||
"searchengine": "Moteur de recherche"
|
||||
},
|
||||
"offline": "Mode Hors-Ligne",
|
||||
"experimental": {
|
||||
"title": "Expérimental",
|
||||
"webp": "Activer WebP",
|
||||
"dark": "Thème sombre",
|
||||
"animations": "Animations"
|
||||
},
|
||||
"language": "Langue",
|
||||
"apply": "Appliquer",
|
||||
"reset": "Reset",
|
||||
"import": "Importer",
|
||||
"export": "Exporter"
|
||||
},
|
||||
"update": {
|
||||
"title": "Actualiser",
|
||||
"offline": {
|
||||
"title": "Hors ligne",
|
||||
"description": "Peut pas se connecter à Internet"
|
||||
},
|
||||
"error": {
|
||||
"title": "Erreur",
|
||||
"content": "Could not connect to the server"
|
||||
},
|
||||
"loading": "Chargement..."
|
||||
},
|
||||
"toasts": {
|
||||
"quote": "Citation Copié",
|
||||
"reset": "Réinitaliser avec succès",
|
||||
"installed": "Installé avec succès",
|
||||
"removed": "Enlevée avec succès",
|
||||
"error": "Quelque chose a mal tourné."
|
||||
},
|
||||
"marketplace": {
|
||||
"photo_packs": "Des Photos Packs",
|
||||
"quote_packs": "Des Citation Packs",
|
||||
"preset_settings": "Paramètres de prédéterminée",
|
||||
"themes": "Thème",
|
||||
"product": {
|
||||
"overview": "Aperçu",
|
||||
"information": "Information",
|
||||
"last_updated": "Dernière mise à jour",
|
||||
"version": "Version",
|
||||
"author": "Auteur",
|
||||
"notice": {
|
||||
"title": "Écriteau",
|
||||
"description": "S'il vous plait, recharger la page."
|
||||
},
|
||||
"buttons": {
|
||||
"addtomue": "Ajouter au Mue",
|
||||
"remove": "Enlever"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
"title": "Vous êtes hors ligne",
|
||||
"description": "Veuillez vous connecter à l'internet."
|
||||
},
|
||||
"see_more": "Voir de Plus"
|
||||
},
|
||||
"addons": {
|
||||
"added": "Ajoutée",
|
||||
"empty": {
|
||||
"title": "Il est vide ici",
|
||||
"description": "Aller su le marché pour en ajouter.",
|
||||
"button": "Naviguer vers le marché"
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/translations/nl.json
Normal file
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Goedemorgen",
|
||||
"afternoon": "Goedemiddag",
|
||||
"evening": "Goedenavond",
|
||||
"christmas": "Fijne kerstdagen",
|
||||
"newyear": "Gelukkig nieuw jaar",
|
||||
"halloween": "Fijne halloween"
|
||||
},
|
||||
"credit": "Afbeelding door",
|
||||
"search": "Zoeken",
|
||||
"modals": {
|
||||
"title": "Opties",
|
||||
"settings": "Instellingen",
|
||||
"addons": "Mijn Extensies",
|
||||
"marketplace": "Marktplaats"
|
||||
},
|
||||
"settings": {
|
||||
"time": {
|
||||
"title": "Tijd",
|
||||
"seconds": "Seconden",
|
||||
"twentyfourhour": "24 uurs",
|
||||
"ampm": "AM/PM (12 uurs)",
|
||||
"zero": "Nul Opvulling",
|
||||
"analog": "Analoog"
|
||||
},
|
||||
"greeting": {
|
||||
"title": "Groeten",
|
||||
"events": "Evenementen",
|
||||
"default": "Standaard begroetingsnaam",
|
||||
"name": "Naam Voor Groet"
|
||||
},
|
||||
"quote": {
|
||||
"title": "Citaat",
|
||||
"copy": "Kopieer Knop"
|
||||
},
|
||||
"background": {
|
||||
"title": "Achtergrond",
|
||||
"API": "Achtergrond API",
|
||||
"blur": "Onscherpte Aanpassen",
|
||||
"customURL": "Aangepaste Achtergrond URL",
|
||||
"custombackground": "Aangepaste Achtergrond",
|
||||
"customcolour": "Aangepaste Achtergrondkleur",
|
||||
"upload": "Uploaden"
|
||||
},
|
||||
"searchbar": {
|
||||
"title": "Zoekbalk",
|
||||
"searchengine": "Zoekmachine"
|
||||
},
|
||||
"offline": "Offline Modus",
|
||||
"experimental": {
|
||||
"title": "Experimenteel",
|
||||
"webp": "WebP Gebruiken",
|
||||
"dark": "Donker Thema",
|
||||
"animations": "Animaties"
|
||||
},
|
||||
"language": "Taal",
|
||||
"apply": "Toepassen",
|
||||
"reset": "Resetten",
|
||||
"import": "Importeren",
|
||||
"export": "Exporteren"
|
||||
},
|
||||
"update": {
|
||||
"title": "Veranderingen",
|
||||
"offline": {
|
||||
"title": "Offline",
|
||||
"description": "Kan veranderingen logboek niet verkrijgen in offline modus"
|
||||
},
|
||||
"error": {
|
||||
"title": "Fout",
|
||||
"content": "Kon niet verbinden met de servers"
|
||||
},
|
||||
"loading": "Aan het laden..."
|
||||
},
|
||||
"toasts": {
|
||||
"quote": "Quote Copied",
|
||||
"reset": "Reset successfully",
|
||||
"installed": "Successvol geïnstalleerd",
|
||||
"removed": "Successvol verwijderd",
|
||||
"error": "Iets is verkeerd gegaan"
|
||||
},
|
||||
"marketplace": {
|
||||
"photo_packs": "Afbeelding Pakketten",
|
||||
"quote_packs": "Citaat Pakketten",
|
||||
"preset_settings": "Vooraf ingestelde instellingen",
|
||||
"themes": "Themas",
|
||||
"product": {
|
||||
"overview": "Overzicht",
|
||||
"information": "Informatie",
|
||||
"last_updated": "Laatst Geüpdatet",
|
||||
"version": "Versie",
|
||||
"author": "Auteur",
|
||||
"notice": {
|
||||
"title": "Aankondiging",
|
||||
"description": "De veranderingen gaan in zodra je de pagina herlaad."
|
||||
},
|
||||
"buttons": {
|
||||
"addtomue": "Voeg aan Mue toe",
|
||||
"remove": "Verwijder"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
"title": "Het ziet er uit dat je offline bent",
|
||||
"description": "Verbind met het internet."
|
||||
},
|
||||
"see_more": "Zie Meer"
|
||||
},
|
||||
"addons": {
|
||||
"added": "Toegevoegd",
|
||||
"empty": {
|
||||
"title": "Het is hier leeg",
|
||||
"description": "Ga naar de marktplaats om een paar extensies toe te veogen.",
|
||||
"button": "Breng me naar de marktplaats"
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/translations/no.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "God Morgen",
|
||||
"afternoon": "God Ettermiddag",
|
||||
"evening": "God Kveld",
|
||||
"christmas": "God Jul",
|
||||
"newyear": "Godt Nyttår",
|
||||
"halloween": "Ha en god Halloween"
|
||||
},
|
||||
"credit": "Bilde av",
|
||||
"search": "Søk",
|
||||
"modals": {
|
||||
"title": "Options",
|
||||
"settings": "Instillinger",
|
||||
"addons": "Mine add-ons",
|
||||
"marketplace": "Markedsplass"
|
||||
},
|
||||
"settings": {
|
||||
"time": {
|
||||
"title": "Tid",
|
||||
"seconds": "Sekunder",
|
||||
"twentyfourhour": "24 Timer",
|
||||
"ampm": "AM/PM (12 Timer)",
|
||||
"zero": "Ekstra Null",
|
||||
"analog": "Analog"
|
||||
},
|
||||
"greeting": {
|
||||
"title": "Hallo",
|
||||
"events": "Planer",
|
||||
"default": "Standard Hallo Melding",
|
||||
"name": "Navn for Hallo"
|
||||
},
|
||||
"quote": {
|
||||
"title": "Sitat",
|
||||
"copy": "Kopier knapp"
|
||||
},
|
||||
"background": {
|
||||
"title": "Bakgrunn",
|
||||
"API": "Bakgrunn API",
|
||||
"blur": "Juster Blur",
|
||||
"customURL": "Personlig bakgrunn URL",
|
||||
"custombackground": "Personlig bakgrunn",
|
||||
"customcolour": "Personlig Bakgrunn Farge"
|
||||
},
|
||||
"searchbar": {
|
||||
"title": "Søkebar",
|
||||
"searchengine": "Søkemotor"
|
||||
},
|
||||
"offline": "Frakoblet Modus",
|
||||
"experimental": {
|
||||
"title": "Eksperimental",
|
||||
"webp": "Skru på WebP",
|
||||
"dark": "Mørkt tema",
|
||||
"animations": "Animasjoner"
|
||||
},
|
||||
"language": "Språk",
|
||||
"apply": "Bruk",
|
||||
"reset": "Nullstill",
|
||||
"import": "Importer",
|
||||
"export": "Eksporter"
|
||||
},
|
||||
"update": {
|
||||
"title": "Oppdater",
|
||||
"offline": {
|
||||
"title": "Offline",
|
||||
"description": "Kan ikke få oppdatering loggene når i offline modus."
|
||||
},
|
||||
"error": {
|
||||
"title": "Error",
|
||||
"content": "Kan ikke koble til serveren."
|
||||
},
|
||||
"loading": "Laster..."
|
||||
},
|
||||
"toasts": {
|
||||
"quote": "Sitat kopiert",
|
||||
"reset": "Tilbakestillte vellykket",
|
||||
"installed": "Installert",
|
||||
"removed": "Fjernet",
|
||||
"error": "Noe gikk galt"
|
||||
},
|
||||
"marketplace": {
|
||||
"photo_packs": "Bilde pakker",
|
||||
"quote_packs": "Sitat pakker",
|
||||
"preset_settings": "Preset instillinger",
|
||||
"themes": "Themes",
|
||||
"product": {
|
||||
"overview": "Oversikt",
|
||||
"information": "Informasjon",
|
||||
"last_updated": "Sist oppdatert",
|
||||
"version": "Versjon",
|
||||
"author": "Forfatter",
|
||||
"notice": {
|
||||
"title": "Vær oppmerksom",
|
||||
"description": "For at endringen skal ta skje, må siden relastes."
|
||||
},
|
||||
"buttons": {
|
||||
"addtomue": "Legg Til Mue",
|
||||
"remove": "Fjern"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
"title": "Ser ut som at du er offiline.",
|
||||
"description": "Vær så snill, koble til internettet."
|
||||
},
|
||||
"see_more": "Se mer"
|
||||
},
|
||||
"addons": {
|
||||
"added": "Lagt til",
|
||||
"empty": {
|
||||
"title": "Det er tomt her.",
|
||||
"description": "Gå til markedsplassen å legg til noe.",
|
||||
"button": "Ta meg der"
|
||||
}
|
||||
}
|
||||
}
|
||||
118
src/translations/ru.json
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"greeting": {
|
||||
"morning": "Доброе утро",
|
||||
"afternoon": "Добрый день",
|
||||
"evening": "Добрый вечер",
|
||||
"christmas": "С Рождеством",
|
||||
"newyear": "С Новым Годом",
|
||||
"halloween": "Счастливого Хэллоуина"
|
||||
},
|
||||
"credit": "Фото",
|
||||
"search": "Поиск",
|
||||
"modals": {
|
||||
"title": "Options",
|
||||
"settings": "Settings",
|
||||
"addons": "My Add-ons",
|
||||
"marketplace": "Marketplace"
|
||||
},
|
||||
"settings": {
|
||||
"time": {
|
||||
"title": "Время",
|
||||
"seconds": "Секунды",
|
||||
"twentyfourhour": "24 Часа",
|
||||
"ampm": "AM/PM (12 часов)",
|
||||
"zero": "Дополнительный ноль",
|
||||
"analog": "Аналоговое"
|
||||
},
|
||||
"greeting": {
|
||||
"title": "Приветствие",
|
||||
"events": "События",
|
||||
"default": "Сообщение с приветствием по-умолчанию",
|
||||
"name": "Имя для приветствия"
|
||||
},
|
||||
"quote": {
|
||||
"title": "Цитата",
|
||||
"copy": "Кнопка копирования"
|
||||
},
|
||||
"background": {
|
||||
"title": "Фон",
|
||||
"API": "API для фона",
|
||||
"blur": "Размытие",
|
||||
"brightness": "Яркость",
|
||||
"customURL": "Ссылка для пользовательского фона",
|
||||
"custombackground": "Пользовательский фон",
|
||||
"customcolour": "Пользовательский цвет фона",
|
||||
"upload": "Загрузить"
|
||||
},
|
||||
"searchbar": {
|
||||
"title": "Панель поиска",
|
||||
"searchengine": "Поисковый движок"
|
||||
},
|
||||
"offline": "Офлайн режим",
|
||||
"experimental": {
|
||||
"title": "Экспериментальные настройки",
|
||||
"webp": "Включить WebP",
|
||||
"dark": "Тёмная тема",
|
||||
"animations": "Анимации"
|
||||
},
|
||||
"language": "Язык",
|
||||
"apply": "Применить",
|
||||
"reset": "Сбросить",
|
||||
"import": "Импорт",
|
||||
"export": "Экспорт",
|
||||
"imported_settings": "Импортированные настройки"
|
||||
},
|
||||
"update": {
|
||||
"title": "Обновление",
|
||||
"offline": {
|
||||
"title": "Офлайн",
|
||||
"description": "Не удалось получить журнал обновления в офлайн режиме"
|
||||
},
|
||||
"error": {
|
||||
"title": "Ошибка",
|
||||
"content": "Не удалось подключиться к серверу"
|
||||
},
|
||||
"loading": "Загрузка..."
|
||||
},
|
||||
"toasts": {
|
||||
"quote": "Цитата скопирована!",
|
||||
"reset": "Сброшено!",
|
||||
"installed": "Успешно установлено",
|
||||
"removed": "Успешно удалено",
|
||||
"error": "Что-то пошло не так"
|
||||
},
|
||||
"marketplace": {
|
||||
"photo_packs": "Наборы фото",
|
||||
"quote_packs": "Наборы цитат",
|
||||
"preset_settings": "Пресеты настроек",
|
||||
"themes": "Темы",
|
||||
"product": {
|
||||
"overview": "Обзор",
|
||||
"information": "Информация",
|
||||
"last_updated": "Последнее обновление",
|
||||
"version": "Версия",
|
||||
"author": "Автор",
|
||||
"notice": {
|
||||
"title": "Предупреждение",
|
||||
"description": "Для того, чтобы изменения вступили в силу необходимо обновить страницу."
|
||||
},
|
||||
"buttons": {
|
||||
"addtomue": "Добавить в Mue",
|
||||
"remove": "Удалить"
|
||||
}
|
||||
},
|
||||
"offline": {
|
||||
"title": "Похоже, что вы офлайн",
|
||||
"description": "Пожалуйста, подключитесь к интернету."
|
||||
},
|
||||
"see_more": "Показать больше"
|
||||
},
|
||||
"addons": {
|
||||
"added": "Добавлен",
|
||||
"empty": {
|
||||
"title": "Здесь пусто",
|
||||
"description": "Зайдите в магазин, чтобы найти что-то интересное.",
|
||||
"button": "Перейти"
|
||||
}
|
||||
}
|
||||
}
|
||||