Compare commits
279 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 | ||
|
|
30dcc789ed | ||
|
|
dc013deaad | ||
|
|
7f49a412aa | ||
|
|
4e774d9ea3 | ||
|
|
30c05f03bf | ||
|
|
2edd9b4b07 | ||
|
|
24e261165b | ||
|
|
39188d5988 | ||
|
|
e175a1edfa | ||
|
|
6f7b6d2337 | ||
|
|
97c6fa58d9 | ||
|
|
ec66e8c9f9 | ||
|
|
35a60f07b6 | ||
|
|
50da14cce6 | ||
|
|
d45fec5028 | ||
|
|
77f2522a80 | ||
|
|
92d0d7efae | ||
|
|
a6c1a21088 | ||
|
|
983a6727e2 | ||
|
|
5da8731017 | ||
|
|
b78f4ccf26 | ||
|
|
2fa7567cf3 | ||
|
|
cde3743ff8 | ||
|
|
1a165f4f1d | ||
|
|
56944b3a07 | ||
|
|
6c39a751a1 | ||
|
|
2f7fd62d5c | ||
|
|
af8223d818 | ||
|
|
298f43bda2 | ||
|
|
7c8c0cfb85 | ||
|
|
1e99676ff8 | ||
|
|
3638e67f28 | ||
|
|
2d42753e77 | ||
|
|
39969cde6d | ||
|
|
40452b71bf | ||
|
|
ab1a7cdf5f | ||
|
|
bdfae155ab | ||
|
|
4ff9ef3545 | ||
|
|
9f21ffe5f0 | ||
|
|
5be47d798b | ||
|
|
c7e19aad03 | ||
|
|
df630e953a | ||
|
|
b5a0cbe0b2 | ||
|
|
aa6400b41f | ||
|
|
735ee6def9 | ||
|
|
9414a35fe8 | ||
|
|
13e5beadf6 | ||
|
|
3c398df411 | ||
|
|
27fb91e9a9 | ||
|
|
5e5214ad0c | ||
|
|
90eb9a424a | ||
|
|
9f961ffdde | ||
|
|
576d829c40 | ||
|
|
c6e9e2226a | ||
|
|
fa117515fc | ||
|
|
70a0ea931d | ||
|
|
d8e2b6cd17 | ||
|
|
379b401d4e | ||
|
|
665a093e41 | ||
|
|
69ebcd7b8b | ||
|
|
4dc7b4564d | ||
|
|
8b809d6f7f | ||
|
|
5cd96e6d10 | ||
|
|
a7516c3b2b | ||
|
|
da04a4f997 | ||
|
|
cbdf5c2a0b | ||
|
|
0881c3de77 | ||
|
|
e5986d2f5b | ||
|
|
6301e548e2 | ||
|
|
4f78262b96 | ||
|
|
6ef77ff0f8 | ||
|
|
8cd9c73ae3 | ||
|
|
85b75f042a | ||
|
|
91eef6ae94 | ||
|
|
bb9e452fea | ||
|
|
6f9a76506d | ||
|
|
dc11ce79e9 | ||
|
|
8e3d72aaa4 | ||
|
|
d34dc6008e | ||
|
|
146223c13b | ||
|
|
5109bb725d | ||
|
|
5883811535 | ||
|
|
48562212e0 | ||
|
|
8b268d680b | ||
|
|
60079f16dc | ||
|
|
72a8bdc28f | ||
|
|
2c3f0a663c | ||
|
|
7f4716f637 | ||
|
|
4ecf43e594 | ||
|
|
d22f7ceb71 | ||
|
|
f6965a8ceb | ||
|
|
541c7dbd78 | ||
|
|
eba3150396 | ||
|
|
7217d0dcea | ||
|
|
7e03b33120 | ||
|
|
9ee061c8fd | ||
|
|
e018a1118a | ||
|
|
fd98cd4af1 | ||
|
|
e0a0f803a2 | ||
|
|
a513a9762a | ||
|
|
cbf63436fb |
59
.eslintrc
@@ -1,59 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"amd": true,
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"commonjs": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"forOf": true,
|
||||
"spread": true,
|
||||
"modules": true,
|
||||
"classes": true,
|
||||
"generators": true,
|
||||
"restParams": true,
|
||||
"regexUFlag": true,
|
||||
"regexYFlag": true,
|
||||
"globalReturn": true,
|
||||
"destructuring": true,
|
||||
"impliedStrict": true,
|
||||
"blockBindings": true,
|
||||
"defaultParams": true,
|
||||
"octalLiterals": true,
|
||||
"arrowFunctions": true,
|
||||
"binaryLiterals": true,
|
||||
"templateStrings": true,
|
||||
"superInFunctions": true,
|
||||
"unicodeCodePointEscapes": true,
|
||||
"objectLiteralShorthandMethods": true,
|
||||
"objectLiteralComputedProperties": true,
|
||||
"objectLiteralDuplicateProperties": true,
|
||||
"objectLiteralShorthandProperties": true
|
||||
}
|
||||
},
|
||||
"plugins": [],
|
||||
"rules": {
|
||||
"semi": "error",
|
||||
"indent": [ 0, 2 ],
|
||||
"strict": "off",
|
||||
"eqeqeq": "error",
|
||||
"no-var": "warn",
|
||||
"no-undef": "warn",
|
||||
"comma-dangle": "warn",
|
||||
"no-dupe-args": "warn",
|
||||
"no-dupe-keys": "warn",
|
||||
"require-await": "warn",
|
||||
"no-global-assign": "warn",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-dupe-class-members": "error"
|
||||
},
|
||||
"globals": {
|
||||
"_config": false,
|
||||
"console": false
|
||||
}
|
||||
}
|
||||
1
.github/FUNDING.yml
vendored
@@ -1,2 +1,3 @@
|
||||
github: ohlookitsderpy
|
||||
patreon: ohlookitsderpy
|
||||
ko_fi: ohlookitsderpy
|
||||
|
||||
34
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug to help improve Mue
|
||||
title: "[Bug]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behaviour:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error in console (Ctrl + Shift + I)
|
||||
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Windows, Ubuntu]
|
||||
- OS Version: [e.g 10, 19.10]
|
||||
- Browser: [e.g. Chrome, Firefox]
|
||||
- Browser Version: [e.g. 70]
|
||||
- Mue Version: [e.g. 0.5]
|
||||
- Resolution: [e.g 1920x1080]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
14
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for Mue
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what you want in this new feature.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
7
.gitignore
vendored
@@ -1,8 +1,9 @@
|
||||
# Directories
|
||||
.vscode/
|
||||
node_modules/
|
||||
.vscode/
|
||||
build/
|
||||
|
||||
# Files
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at github@muetab.xyz. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
42
LICENSE
@@ -1,21 +1,29 @@
|
||||
MIT License
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018-2019 David Ralph (ohlookitsderpy)
|
||||
Copyright (c) 2018-2020 Mue Tab
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
152
README.md
@@ -1,67 +1,99 @@
|
||||
<img src='assets/logo/logo_horizontal.png'>
|
||||
<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://www.codacy.com/app/ohlookitsderpy/Mue?utm_source=github.com&utm_medium=referral&utm_content=ohlookitsderpy/Mue&utm_campaign=Badge_Grade)
|
||||
[](https://discord.gg/HJmmmTB)
|
||||
> <a href="https://muetab.xyz/">Mue</a>
|
||||
|
||||
<a href='https://ko-fi.com/ohlookitsderpy' target='_blank'><img height='36' src='assets/kofi.png' alt='Buy me a Coffee at ko-fi.com' /></a>
|
||||
<a href='https://patreon.com/ohlookitsderpy' target='_blank'><img height='36' src='assets/patreon.png' alt='Become a patron on patreon.com' /></a>
|
||||
[](/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)
|
||||
|
||||
Fast, open and free-to-use new tab page for most modern browsers
|
||||
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
|
||||
* Multiple language support
|
||||
* 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!
|
||||
|
||||
### 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
|
||||
<a href='https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid'><img src='assets/chrome.png' target='_blank'></a>
|
||||
[](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
|
||||
<a href='https://addons.mozilla.org/firefox/addon/mue'><img src='assets/firefox.png' target='_blank'></a>
|
||||
[](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
|
||||
Development: Read the [Development](#development) section
|
||||
[GitHub Releases](https://github.com/mue/mue/releases)
|
||||
|
||||
### Development
|
||||
<h5>Requirements</h5>
|
||||
#### Requirements
|
||||
<ol>
|
||||
<li>Git (optional)</li>
|
||||
<li>Node.js</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/ohlookitsderpy/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 install</code>)
|
||||
<li> <code>yarn run sass-watch</code> (or <code>npm run sass-watch</code>)
|
||||
<li>Open another terminal and run this command: (in the Mue directory)
|
||||
<li> <code>yarn run js-watch</code> (or <code>npm run js-watch</code>)
|
||||
<li>Start developing! (See the sections below for how to set it up with your browser)
|
||||
<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</h2>
|
||||
<details>
|
||||
<summary><b>Chrome</b> (Click to expand)</summary>
|
||||
<summary><b>Chrome/Edge (Chromium)</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<li> Rename <code>manifest-chrome.json</code> to <code>manifest.json</code>
|
||||
<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 Mue and click <b>ok</b>
|
||||
<li> Go to the directory containing the built copy of Mue and click <b>ok</b>
|
||||
<li> Enjoy your new tab!
|
||||
</details>
|
||||
<details>
|
||||
<summary><b>Opera</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<li> Rename <code>manifest-opera.json</code> to <code>manifest.json</code>
|
||||
<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>
|
||||
@@ -69,9 +101,9 @@ Development: Read the [Development](#development) section
|
||||
</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> Rename <code>manifest-firefox.json</code> to <code>manifest.json</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>
|
||||
@@ -82,45 +114,37 @@ Development: Read the [Development](#development) section
|
||||
<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*
|
||||
|
||||
<img src='assets/screenshot.jpg'>
|
||||
|
||||
## Credits
|
||||
### Maintainers
|
||||
[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) - Portions of original code, name idea
|
||||
### Contributors
|
||||
[Wessel Tip](https://github.com/Wessel) - Development <br>
|
||||
[Isaac (Eartharoid)](https://github.com/eartharoid) - QA, Development, Photographer <br>
|
||||
|
||||
[Pexels](https://pexels.com) - Stock photos used
|
||||
### 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
|
||||
|
||||
[Animate.css](https://daneden.github.io/animate.css) - Portions of code used to add animations
|
||||
[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/Chivi19) - Italian (Quotes and Messages)
|
||||
|
||||
Pepehound - Spanish (Quotes and Messages)
|
||||
|
||||
Candystick - Portuguese (Some Quotes)
|
||||
|
||||
[PassTheWessel](https://github.com/PassTheWessel) - Dutch (Messages)
|
||||
|
||||
[Yanderella](https://github.com/Chivi19) 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)
|
||||
|
||||
[MrSheldon](https://github.com/MrSheldon) - Arabic and Swedish (Messages)
|
||||
|
||||
*Feel free to pull request with other translations!*
|
||||
|
||||
and all the contributors <3
|
||||
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
|
||||
|
||||
BIN
assets/kofi.png
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 925 B |
|
Before Width: | Height: | Size: 351 KiB After Width: | Height: | Size: 262 KiB |
14
manifest/background-opera.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable no-undef */
|
||||
// Original code sourced from https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14
|
||||
|
||||
chrome.tabs.onCreated.addListener((tab) => {
|
||||
if (tab.status === 'complete' && tab.url === 'chrome://startpage/') chrome.tabs.update(tab.id, {
|
||||
url: chrome.extension.getURL('index.html')
|
||||
});
|
||||
});
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (changeInfo.status === 'complete' && tab.url === 'chrome://startpage/') chrome.tabs.update(tabId, {
|
||||
url: chrome.extension.getURL('index.html')
|
||||
});
|
||||
});
|
||||
18
manifest/chrome.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"offline_enabled": true,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "index.html"
|
||||
},
|
||||
"icons": {
|
||||
"16": "icons/16x16.png",
|
||||
"48": "icons/48x48.png",
|
||||
"128": "icons/128x128.png"
|
||||
}
|
||||
}
|
||||
15
manifest/firefox.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "index.html"
|
||||
},
|
||||
"chrome_settings_overrides": {
|
||||
"homepage": "index.html"
|
||||
}
|
||||
}
|
||||
17
manifest/opera.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"version": "4.1.0",
|
||||
"browser_action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"./background-opera.js"
|
||||
]
|
||||
},
|
||||
"permissions": [
|
||||
"tabs"
|
||||
]
|
||||
}
|
||||
71
package.json
@@ -1,17 +1,54 @@
|
||||
{
|
||||
"name": "mue",
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers",
|
||||
"author": "ohlookitsderpy",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/ohlookitsderpy/Mue",
|
||||
"scripts": {
|
||||
"sass-watch": "sass --watch src/assets/scss:src/assets/css --style compressed",
|
||||
"js-watch": "webpack --progress --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "6.0.0",
|
||||
"sass": "1.20.3",
|
||||
"webpack": "4.32.2",
|
||||
"webpack-cli": "3.3.4"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "mue",
|
||||
"private": true,
|
||||
"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/alexsparkes)"
|
||||
],
|
||||
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
|
||||
"repository": {
|
||||
"url": "github:mue/mue"
|
||||
},
|
||||
"homepage": "https://muetab.xyz",
|
||||
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "4.1.0",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@muetab/quotes": "^1.0.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": {
|
||||
"react-scripts": "3.4.3",
|
||||
"node-sass": "^4.14.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"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"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/fonts/LexendDeca-Regular.woff2
Normal file
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 |
BIN
public/icons/32x32.png
Normal file
|
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 |
16
public/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE 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>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to use Mue.</noscript>
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 737 KiB After Width: | Height: | Size: 737 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 390 KiB After Width: | Height: | Size: 390 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 495 KiB |
|
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 472 KiB |
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
126
src/App.jsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
|
||||
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/>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
9
src/assets/css/animate.css
vendored
@@ -1,9 +0,0 @@
|
||||
/*!
|
||||
* animate.css -http://daneden.me/animate
|
||||
* Version - 3.7.0
|
||||
* Licensed under the MIT license - http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Copyright (c) 2018 Daniel Eden
|
||||
* Modified by ohlookitsderpy
|
||||
*/
|
||||
@-webkit-keyframes animate{from{opacity:0;-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0)}to{opacity:1;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}}@keyframes animate{from{opacity:0;-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0)}to{opacity:1;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}}.animate{-webkit-animation-name:amimated;animation-name:animate;-webkit-animation-duration:0.75s;animation-duration:0.75s;-webkit-animation-fill-mode:both;animation-fill-mode:both}
|
||||
@@ -1 +0,0 @@
|
||||
.mountain{background-image:url("../img/1.jpeg")}.sunrise{background-image:url("../img/2.jpeg")}.butterfly{background-image:url("../img/3.jpeg")}.leaves{background-image:url("../img/4.jpeg")}.river{background-image:url("../img/5.jpeg")}.sea{background-image:url("../img/6.jpeg")}.space{background-image:url("../img/7.jpeg")}.ice{background-image:url("../img/8.jpeg")}.waterfall{background-image:url("../img/9.jpeg")}.lake{background-image:url("../img/10.jpeg")}.sunset{background-image:url("../img/11.jpeg")}.desert{background-image:url("../img/12.jpeg")}.canyon{background-image:url("../img/13.jpeg")}.rose{background-image:url("../img/14.jpeg")}.forest{background-image:url("../img/15.jpeg")}.cherry{background-image:url("../img/16.jpeg")}.clouds{background-image:url("../img/17.jpeg")}.winter{background-image:url("../img/18.jpeg")}.autumn{background-image:url("../img/19.jpeg")}.flowers{background-image:url("../img/20.jpeg")}.sunrise{background-image:url("../img/21.jpeg")}.rocks{background-image:url("../img/22.jpeg")}.trees{background-image:url("../img/23.jpeg")}.mountains{background-image:url("../img/24.jpeg")}.beach{background-image:url("../img/25.jpeg")}html,body{height:100%}body{display:flex;flex-direction:column;justify-content:space-around;align-items:center;margin:0;background-size:cover;background-position:50%;background-color:#000;font-family:"Roboto";color:#fff;overflow:hidden;-moz-user-select:none;user-select:none}@font-face{font-family:"Roboto";font-style:normal;font-weight:400;src:local("Roboto"),local("Roboto-Regular"),url("../font/roboto-v18-latin-regular.woff2") format("woff2")}:root{--shadow-color: #111111CC}.greeting,time,.quote{filter:drop-shadow(var(--shadow-shift) var(--shadow-shift) 0 var(--shadow-color))}.quote{--shadow-shift: 0.125rem;width:37.5%;max-width:37.5rem;font-size:1.5rem;text-align:center}.quote blockquote{margin:0 0 .25rem 0;line-height:1.125}.quote blockquote ::before{content:"“";margin-right:.25ch}.quote blockquote ::after{content:"”";margin-left:.25ch}.quote cite{font-size:83.33%}.greeting{--shadow-shift: 0.2rem;font-size:3.25rem}time{--shadow-shift: 0.4rem;font-size:12rem}/*# sourceMappingURL=base.css.map */
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../scss/modules/_bg.scss","../scss/modules/_misc.scss","../scss/modules/_quote.scss","../scss/modules/_time-greeting.scss"],"names":[],"mappings":"CAkBA,gDACA,+CACA,iDACA,8CACA,6CACA,2CACA,6CACA,2CACA,iDACA,6CACA,+CACA,+CACA,+CACA,6CACA,+CACA,+CACA,+CACA,+CACA,+CACA,gDACA,gDACA,8CACA,8CACA,kDACA,8CCzCA,sBACA,KAEC,aACA,sBACA,6BACA,mBAEA,SAEA,sBACA,wBACA,sBAEA,qBACA,WAEA,gBAEG,sBACH,iBAID,WACC,qBACA,kBACA,gBACA,0GAKD,MACC,0BAGD,sBAGC,kFCvBD,OACI,yBAEA,YACA,kBAEA,iBACA,kBAEA,kBACI,oBACA,kBAEA,2BACI,YACA,mBAGJ,0BACI,YACA,kBAIR,YACI,iBCvBR,UACC,uBACA,kBAKD,KACC,uBACA","file":"base.css"}
|
||||
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 401 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,9 +0,0 @@
|
||||
// Original code sourced from https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14
|
||||
|
||||
chrome.tabs.onCreated.addListener((tab) => {
|
||||
if (tab.status === 'complete' && tab.url === 'chrome://startpage/') chrome.tabs.update(tab.id, { url: chrome.extension.getURL('index.html') });
|
||||
});
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (changeInfo.status === 'complete' && tab.url === 'chrome://startpage/') chrome.tabs.update(tabId, { url: chrome.extension.getURL('index.html') });
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
const func = require('./modules/func.js');
|
||||
|
||||
const init = () => {
|
||||
// init() gets executed only when the page is fully loaded
|
||||
func.setDaytimeMsg();
|
||||
func.setRandBg();
|
||||
func.setRandQuote();
|
||||
func.setTime();
|
||||
// set interval to update time every second
|
||||
setInterval(func.setTime, 1000);
|
||||
};
|
||||
|
||||
// initialize on page load through a listener
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
// Disable right click
|
||||
const rightClick = () => { return false; };
|
||||
document.oncontextmenu=rightClick;
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
module.exports = [
|
||||
'mountain',
|
||||
'sunrise',
|
||||
'butterfly',
|
||||
'leaves',
|
||||
'river',
|
||||
'sea',
|
||||
'space',
|
||||
'ice',
|
||||
'waterfall',
|
||||
'lake',
|
||||
'sunset',
|
||||
'desert',
|
||||
'canyon',
|
||||
'rose',
|
||||
'forest',
|
||||
'cherry',
|
||||
'clouds',
|
||||
'autumn',
|
||||
'winter',
|
||||
'flowers',
|
||||
'sunrise',
|
||||
'rocks',
|
||||
'trees',
|
||||
'mountains',
|
||||
'beach'
|
||||
];
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
itcodes: ['it', 'it-IT', 'it-CH'],
|
||||
nlcodes: ['nl', 'nl-BE'],
|
||||
frcodes: ['fr', 'fr-BE', 'fr-CA', 'fr-FR', 'fr-LU', 'fr-MC', 'fr-CH'],
|
||||
ptcodes: ['pt', 'pt-BR'],
|
||||
spcodes: ['es', 'es-AR', 'es-BO', 'es-CL', 'es-CO', 'es-CR', 'es-DO', 'es-EC', 'es-ES', 'es-GT', 'es-HN', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PR', 'es-PY', 'es-SV', 'es-UY', 'es-VE'],
|
||||
ficodes: ['fi'],
|
||||
decodes: ['de', 'de-AT', 'de-DE', 'de-LI', 'de-LU', 'de-CH'],
|
||||
hecodes: ['he'],
|
||||
rucodes: ['ru', 'ru-MO'],
|
||||
arcodes: ['ar', 'ar-DZ', 'ar-BH', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-QA', 'ar-SA', 'ar-SY', 'ar-TN', 'ar-AE', 'ar-YE'],
|
||||
svcodes: ['sv', 'sv-FI', 'sv-SV']
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
const bg = require('./bg.js');
|
||||
const msg = require('./msg.js');
|
||||
const util = require('./util.js');
|
||||
const codes = require('./code.js');
|
||||
const quotes = require('./quote.js');
|
||||
|
||||
const nal = navigator.language;
|
||||
|
||||
module.exports = class Func {
|
||||
static setDaytimeMsg() {
|
||||
if (util.contains.call(codes.itcodes, nal)) msg.itMsgSet(); // Italian
|
||||
if (util.contains.call(codes.nlcodes, nal)) msg.nlMsgSet(); // Dutch
|
||||
if (util.contains.call(codes.frcodes, nal)) msg.frMsgSet(); // French
|
||||
//else if (util.contains.call(codes.ptcodes, nal)) msg.ptMsgSet(); // Portuguese
|
||||
if (util.contains.call(codes.spcodes, nal)) msg.spMsgSet(); // Spanish
|
||||
if (util.contains.call(codes.ficodes, nal)) msg.fiMsgSet(); // Finnish
|
||||
if (util.contains.call(codes.decodes, nal)) msg.spMsgSet(); // German
|
||||
if (util.contains.call(codes.hecodes, nal)) msg.heMsgSet(); // Hebrew
|
||||
if (util.contains.call(codes.rucodes, nal)) msg.ruMsgSet(); // Russian
|
||||
if (util.contains.call(codes.arcodes, nal)) msg.arMsgSet(); // Arabic
|
||||
if (util.contains.call(codes.svcodes, nal)) msg.svMsgSet(); // Swedish
|
||||
else msg.engMsgSet(); // English
|
||||
}
|
||||
|
||||
static setRandBg() {
|
||||
document.body.classList.add(util.pickFromArray(bg));
|
||||
}
|
||||
|
||||
static setRandQuote() {
|
||||
const id = util.getRandIndex(quotes.authors);
|
||||
if (util.contains.call(codes.itcodes, nal)) document.querySelector('blockquote').innerHTML = `"${quotes.it[id]}"`; // Italian
|
||||
if (util.contains.call(codes.ptcodes, nal)) document.querySelector('blockquote').innerHTML = `"${quotes.pt[id]}"` || `"${quotes.eng[id]}"`; // Portuguese
|
||||
if (util.contains.call(codes.spcodes, nal)) document.querySelector('blockquote').innerHTML = `"${quotes.sp[id]}"`; // Spanish
|
||||
else document.querySelector('blockquote').innerHTML = `"${quotes.eng[id]}"`; // English
|
||||
document.querySelector('cite').innerHTML = quotes.authors[id];
|
||||
}
|
||||
|
||||
static setTime() {
|
||||
const date = new Date(),
|
||||
time = [
|
||||
util.formatTimeUnit(date.getHours()),
|
||||
util.formatTimeUnit(date.getMinutes()),
|
||||
util.formatTimeUnit(date.getSeconds())
|
||||
];
|
||||
// Joins all of the array elements into a string using the ':' separator
|
||||
// Example: [16, 32, 03] -> "16:32:03"
|
||||
document.querySelector('time').innerHTML = time.join(':');
|
||||
}
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
const hour = new Date().getHours(); // Get the current hour
|
||||
|
||||
module.exports = class Msg {
|
||||
// English
|
||||
static engMsgSet() {
|
||||
let time = 'Good evening'; // Set the default time string to "Good evening"
|
||||
if (hour < 12) time = 'Good morning'; //If it's before 12am, set the time string to "Good morning"
|
||||
else if (hour < 18) time = 'Good afternoon'; //If it's before 6pm, set the time string to "Good afternoon"
|
||||
document.querySelector('.greeting').innerHTML = time; // Write the string contents to the HTML
|
||||
}
|
||||
|
||||
// Italian
|
||||
static itMsgSet() {
|
||||
let time = 'Buongiorno'; // Set the default time string
|
||||
if (hour > 18) time = 'Buonasera'; // In Italian there is just Buongiorno or Buonasera, if it's before 6pm then set the time string to Buonasera
|
||||
document.querySelector('.greeting').innerHTML = time; // Write the string contents to the HTML
|
||||
}
|
||||
|
||||
// Dutch
|
||||
static nlMsgSet() { // Everything below is the same as English, check the comments there for information
|
||||
let time = 'Goedemiddag';
|
||||
if (hour < 12) time = 'Goedemorgen';
|
||||
else if (hour > 18) time = 'Goedenavond';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// French
|
||||
static frMsgSet() {
|
||||
let time = 'Bonsoir';
|
||||
if (hour < 12) time = 'Bonjour';
|
||||
else if (hour > 18) time = 'Bonne après-midi';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// Spanish
|
||||
static spMsgSet() {
|
||||
let time = 'Buenas Tardes'; // Set the default time string to "Good evening"
|
||||
if (hour < 12) time = 'Buenos Días'; // If it's before 12am, set the time string to "Good morning"
|
||||
else if (hour > 18) time = 'Buenas Noches'; // If it's before 6pm, set the time string to "Good afternoon"
|
||||
document.querySelector('.greeting').innerHTML = time; // Write the string contents to the HTML
|
||||
}
|
||||
|
||||
// Finnish
|
||||
static fiMsgSet() {
|
||||
let time = 'Hyvää iltaa'; // Set the default time string to "Good evening"
|
||||
if (hour < 12) time = 'Hyvää huomenta'; // If it's before 12am, set the time string to "Good morning"
|
||||
else if (hour > 18) time = 'Hyvää iltapäivää'; // If it's before 6pm, set the time string to "Good afternoon"
|
||||
document.querySelector('.greeting').innerHTML = time; // Write the string contents to the HTML
|
||||
}
|
||||
|
||||
// German
|
||||
static deMsgSet() {
|
||||
let time = 'Guten Abend';
|
||||
if (hour < 12) time = 'Guten Morgen';
|
||||
else if (hour > 18) time = 'Guten Nachmittag';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// Hebrew
|
||||
static heMsgSet() {
|
||||
let time = 'ערב טוב';
|
||||
if (hour < 12) time = 'בוקר טוב';
|
||||
else if (hour > 18) time = 'אחר הצהריים טובים';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// Russian
|
||||
static ruMsgSet() {
|
||||
let time = 'Добрый Вечер';
|
||||
if (hour < 12) time = 'добрый утро';
|
||||
else if (hour > 18) time = 'добрый день';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// Arabic
|
||||
static arMsgSet() {
|
||||
let time = 'مساء الخير';
|
||||
if (hour < 12) time = 'صباح الخير';
|
||||
else if (hour > 18) time = 'مساء الخير';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
|
||||
// Swedish
|
||||
static svMsgSet() {
|
||||
let time = 'God kväll';
|
||||
if (hour < 12) time = 'God morgon';
|
||||
document.querySelector('.greeting').innerHTML = time;
|
||||
}
|
||||
};
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
eng: [
|
||||
'Time goes on. So whatever you’re going to do, do it. Do it now. Don’t wait.',
|
||||
'All our dreams can come true, if we have the courage to pursue them.',
|
||||
'It does not matter how slowly you go as long as you do not stop.',
|
||||
'Believe in yourself. You are braver than you think, more talented than you know, and capable of more than you imagine.',
|
||||
'If you believe it will work out, you’ll see opportunities. If you believe it won’t, you will see obstacles',
|
||||
'Everything you’ve ever wanted is on the other side of fear.',
|
||||
'Success is not final, failure is not fatal: it is the courage to continue that counts.',
|
||||
'There is only one thing that makes a dream impossible to achieve: the fear of failure.',
|
||||
'Your true success in life begins only when you make the commitment to become excellent at what you do.',
|
||||
'Believe in yourself, take on your challenges, dig deep within yourself to conquer fears. Never let anyone bring you down. You got to keep going.',
|
||||
'Too many of us are not living our dreams because we are living our fears.',
|
||||
'Hard times don’t create heroes. It is during the hard times when the ‘hero’ within us is revealed.',
|
||||
'If you can tune into your purpose and really align with it, setting goals so that your vision is an expression of that purpose, then life flows much more easily.',
|
||||
'Whatever the mind can conceive and believe, it can achieve.',
|
||||
'Don’t wish it were easier. Wish you were better.',
|
||||
'A champion is defined not by their wins but by how they can recover when they fall.',
|
||||
'Motivation comes from working on things we care about.',
|
||||
'With the right kind of coaching and determination you can accomplish anything.',
|
||||
'Some people look for a beautiful place. Others make a place beautiful.',
|
||||
'Life is like riding a bicycle. To keep your balance, you must keep moving.'
|
||||
|
||||
],
|
||||
ita: [
|
||||
'Il tempo passa. Quindi qualunque cosa che farai, falla. Falla ora. Non aspettare',
|
||||
'Tutti i nostri sogni possono diventare reali, se abbiamo il coraggio di seguirli.',
|
||||
'Non importa quanto lentamente vai fino a quando non ti fermi',
|
||||
'Credi in te stesso. Sei più coraggioso di quanto pensi, più talentuoso di quanto credi, e capace più di quanto puoi immaginare.',
|
||||
'Se ci credi funzionerà, vedrai delle opportunità. Se non ci credi, vedrai solamente ostacoli',
|
||||
'Tutti i tuoi desideri sono opposti alla paura',
|
||||
'Il successo non è la fine, il fallimento non è fatale: è il coraggio per continuare quello che conta.',
|
||||
"C'è solo una cosa che fa i sogni impossibili: la paura di fallire",
|
||||
'Il vero successo nella tua vita inizia solo quando fai il sacrificio per diventare eccellente a quello che ami.',
|
||||
"Credi in te stesso, sfida i tuoi problemi, scava nel profondo del tuo io per sconfiggere le tue paure. Mai arrendersi per qualcun'altro. Tu devi continuare.",
|
||||
"Troppe persone non vivono i loro sogni per vivere nelle loro paure",
|
||||
"Tempi difficili non fanno eroi. È durante i tempi duri che \"l'eroe\" in noi viene rivelato.",
|
||||
"Se puoi sintonizzare sul tuo senso e allinearti a quest'ultimo, impostando i tuoi obiettivi in modo che la tua visione sia un'espressione di quel senso, La tua vita scorre molto più facilmente",
|
||||
"Qualunque cosa la mente può immaginare e crederese, si può realizzare",
|
||||
"Non desiderare che fosse stato più facile. Desidera che tu fossi stato migliore.",
|
||||
"Un campione si definisce non dalle sue vittorie ma da come recupera quando cade",
|
||||
"La motivazione viene dal lavorare so cose che amiamo",
|
||||
"Con il giusto tipo di allenamento e determinazione puoi fare tutto",
|
||||
"Alcune persone cercano un posto indimenticabile. Altre lo transformano in un posto mozzafiato.",
|
||||
"La vita è come andare in bicicletta. Per tenerti in equilibrio, devi continuare a muoverti"
|
||||
],
|
||||
spa: [
|
||||
'El tiempo continúa. Así que lo que sea que vayas a hacer, hazlo. Hazlo ahora. No esperes',
|
||||
'Todos nuestros sueños pueden hacerse realidad, si tenemos el coraje de perseguirlos.',
|
||||
'No importa qué tan lento vayas, siempre y cuando no te detengas.',
|
||||
'Cree en ti mismo. Eres más valiente de lo que crees, más talentoso de lo que sabes y capaz de más de lo que imaginas.',
|
||||
'Si crees que funcionará, verás oportunidades. Si crees que no, verás obstáculos ',
|
||||
'Todo lo que siempre has querido está al otro lado del miedo',
|
||||
'El éxito no es definitivo, el fracaso no es fatal: el coraje para continuar es lo que cuenta',
|
||||
'Solo hay una cosa que hace que un sueño sea imposible de lograr: el miedo al fracaso',
|
||||
'Tu verdadero éxito en la vida comienza solo cuando te comprometes a ser excelente en lo que haces',
|
||||
'Cree en ti mismo, asume tus desafíos, excava profundo dentro de ti mismo para vencer tus miedos. Nunca dejes que nadie te derribe. Tienes que seguir adelante.',
|
||||
'Muchos de nosotros no estamos viviendo nuestros sueños porque estamos viviendo nuestros miedos',
|
||||
'Los tiempos difíciles no crean héroes. Es durante los momentos difíciles en que se revela el héroe dentro de nosotros.',
|
||||
'Si puedes sincornizarte con tu propósito, y realmente alinearte con él, estableciendo metas para que tu visión sea una expresión de ese propósito, entonces la vida fluye mucho más fácilmente',
|
||||
'Lo que la mente pueda concebir y creer, lo puede lograr',
|
||||
'No desees que sea fácil. Desea ser mejor.',
|
||||
'Un campeón se define no por sus victorias, sino por cómo pueden recuperarse cuando caen',
|
||||
'La motivación viene de trabajar en cosas que nos importan',
|
||||
'Con el entrenamiento y la determinación adecuados, puedes lograr cualquier cosa',
|
||||
'Algunas personas buscan un lugar hermoso. Otras, hacen un lugar hermoso.'
|
||||
],
|
||||
pt: [
|
||||
'O tempo continua. Então o que quer que você vai fazer,faça. Faça agora. Não espere.',
|
||||
'Todos os sonhos podem virar verdade,se tivermos a coragem de persegui-los.',
|
||||
'Não importa o quão devagar você for,desde que você não pare.',
|
||||
'Acredite em si mesmo. Você é mais corajoso que pensa,mais talentoso que sabe,e capaz de mais que imagina.',
|
||||
'Se você acredita que vai dar certo,você verá oportunidades. Se você acredita que não vai,você vera obstáculos.'
|
||||
],
|
||||
authors: [
|
||||
'Robert De Niro',
|
||||
'Walt Disney',
|
||||
'Confucius',
|
||||
'Roy T. Bennett',
|
||||
'Wayne Dyer',
|
||||
'George Addair',
|
||||
'Winston Churchill',
|
||||
'Paulo Coelho',
|
||||
'Brian Tracy',
|
||||
'Chantal Sutherland',
|
||||
'Les Brown',
|
||||
'Bob Riley',
|
||||
'Jack Canfield',
|
||||
'Napoleon Hill',
|
||||
'Jim Rohn',
|
||||
'Serena Williams',
|
||||
'Sheryl Sandberg',
|
||||
'Reese Witherspoon',
|
||||
'Hazrat Inayat Khan',
|
||||
'Albert Einstein'
|
||||
]
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
module.exports = class Util {
|
||||
// Format time
|
||||
static formatTimeUnit(unit) {
|
||||
return unit < 10 ? '0' + unit : unit;
|
||||
}
|
||||
|
||||
// Get random item
|
||||
static getRandIndex(array) {
|
||||
return Math.floor(Math.random() * (array.length - 1));
|
||||
}
|
||||
|
||||
// Pick random from array
|
||||
static pickFromArray(array) {
|
||||
return array[Math.floor(Math.random() * (array.length - 1))];
|
||||
}
|
||||
|
||||
static contains(needle) {
|
||||
const findNaN = needle !== needle;
|
||||
let indexOf;
|
||||
if (!findNaN && typeof Array.prototype.indexOf === 'function') indexOf = Array.prototype.indexOf;
|
||||
else {
|
||||
indexOf = (needle) => {
|
||||
let i = -1,
|
||||
index = -1;
|
||||
for (i = 0; i < this.length; i++) {
|
||||
const item = this[i];
|
||||
if ((findNaN && item !== item) || item === needle) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
};
|
||||
}
|
||||
return indexOf.call(this, needle) > -1;
|
||||
}
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
@import 'modules/bg';
|
||||
@import 'modules/misc';
|
||||
@import 'modules/quote';
|
||||
@import 'modules/time-greeting';
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
.mountain { background-image: url('../img/1.jpeg'); }
|
||||
.sunrise { background-image: url('../img/2.jpeg'); }
|
||||
.butterfly { background-image: url('../img/3.jpeg'); }
|
||||
.leaves { background-image: url('../img/4.jpeg'); }
|
||||
.river { background-image: url('../img/5.jpeg'); }
|
||||
.sea { background-image: url('../img/6.jpeg'); }
|
||||
.space { background-image: url('../img/7.jpeg'); }
|
||||
.ice { background-image: url('../img/8.jpeg'); }
|
||||
.waterfall { background-image: url('../img/9.jpeg'); }
|
||||
.lake { background-image: url('../img/10.jpeg'); }
|
||||
.sunset { background-image: url('../img/11.jpeg'); }
|
||||
.desert { background-image: url('../img/12.jpeg'); }
|
||||
.canyon { background-image: url('../img/13.jpeg'); }
|
||||
.rose { background-image: url('../img/14.jpeg'); }
|
||||
.forest { background-image: url('../img/15.jpeg'); }
|
||||
.cherry { background-image: url('../img/16.jpeg'); }
|
||||
.clouds { background-image: url('../img/17.jpeg'); }
|
||||
.winter { background-image: url('../img/18.jpeg'); }
|
||||
.autumn { background-image: url('../img/19.jpeg'); }
|
||||
.flowers { background-image: url('../img/20.jpeg'); }
|
||||
.sunrise { background-image: url('../img/21.jpeg'); }
|
||||
.rocks { background-image: url('../img/22.jpeg'); }
|
||||
.trees { background-image: url('../img/23.jpeg'); }
|
||||
.mountains { background-image: url('../img/24.jpeg'); }
|
||||
.beach { background-image: url('../img/25.jpeg'); }
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Body */
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
margin: 0;
|
||||
|
||||
background-size: cover;
|
||||
background-position: 50%;
|
||||
background-color: black; /* Prevent white flash on page load */
|
||||
|
||||
font-family: 'Roboto';
|
||||
color: white;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
-moz-user-select: none; /* Firefox */
|
||||
user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
|
||||
}
|
||||
|
||||
/* Font */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url('../font/roboto-v18-latin-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* Shadow */
|
||||
:root {
|
||||
--shadow-color: #111111CC;
|
||||
}
|
||||
|
||||
.greeting,
|
||||
time,
|
||||
.quote {
|
||||
filter: drop-shadow(var(--shadow-shift) var(--shadow-shift) 0 var(--shadow-color));
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
.quote {
|
||||
--shadow-shift: 0.125rem;
|
||||
|
||||
width: 37.5%;
|
||||
max-width: 37.5rem; /* 600px on desktop */
|
||||
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0.25rem 0;
|
||||
line-height: 1.125;
|
||||
|
||||
::before {
|
||||
content: '“';
|
||||
margin-right: 0.25ch;
|
||||
}
|
||||
|
||||
::after {
|
||||
content: '”';
|
||||
margin-left: 0.25ch;
|
||||
}
|
||||
}
|
||||
|
||||
cite {
|
||||
font-size: 83.33%;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
*/
|
||||
|
||||
/* Greeting */
|
||||
|
||||
.greeting {
|
||||
--shadow-shift: 0.2rem;
|
||||
font-size: 3.25rem;
|
||||
}
|
||||
|
||||
/* Time */
|
||||
|
||||
time {
|
||||
--shadow-shift: 0.4rem;
|
||||
font-size: 12rem;
|
||||
}
|
||||
24
src/components/Navbar.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
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.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'>
|
||||
<Gear className='settings-icon' onClick={this.props.settingsModalOpen} />
|
||||
</div>
|
||||
{refreshHTML}
|
||||
<div className={refresh === 'false' ? 'navbar2' : 'navbar3'}>
|
||||
<NewReleases className='refreshicon' onClick={this.props.updateModalOpen} />
|
||||
</div>
|
||||
</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,52 +0,0 @@
|
||||
<!--
|
||||
█████████████████████████████████████████████████████████████
|
||||
██ ██
|
||||
██ ███ ███ ██ ██ ███████ ██
|
||||
██ ████ ████ ██ ██ ██ ██
|
||||
██ ██ ████ ██ ██ ██ █████ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██ ██████ ███████ ██
|
||||
██ ██
|
||||
██ ██
|
||||
██ Copyright 2018-2019 David Ralph (ohlookitsderpy) ██
|
||||
██ Licensed under MIT ██
|
||||
██ GitHub: https://github.com/ohlookitsderpy/Mue ██
|
||||
██ ██
|
||||
██ Special thanks to contributors! <3 ██
|
||||
█████████████████████████████████████████████████████████████
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>New Tab</title>
|
||||
|
||||
<link href='./assets/css/base.css' rel='stylesheet'>
|
||||
<link href='./assets/css/animate.css' rel='stylesheet'>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class='greeting animate'></div>
|
||||
|
||||
<time class='animate'></time>
|
||||
|
||||
<div class='quote animate'>
|
||||
|
||||
<blockquote></blockquote>
|
||||
|
||||
<cite></cite>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script src='./assets/js/base.js'></script>
|
||||
|
||||
</html>
|
||||