Compare commits
526 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bf6cca8c6 | ||
|
|
096df4f073 | ||
|
|
490f42a9ad | ||
|
|
ef2f666ccf | ||
|
|
afcead634b | ||
|
|
98c857e7ad | ||
|
|
34cf696bb4 | ||
|
|
2f3252d15e | ||
|
|
7ce2afb773 | ||
|
|
203ff27ee1 | ||
|
|
50b1d16171 | ||
|
|
13825c5525 | ||
|
|
a91d5843b9 | ||
|
|
fb15a1037b | ||
|
|
c66add33d2 | ||
|
|
6ec57c75d4 | ||
|
|
54e7a3a33f | ||
|
|
7e2432b9b4 | ||
|
|
640b83e1c3 | ||
|
|
4d3c7cbbe6 | ||
|
|
23251d248b | ||
|
|
ba31ea5841 | ||
|
|
4f5233fab9 | ||
|
|
5e4a14ba2c | ||
|
|
e82ac7da9e | ||
|
|
16485c3d2c | ||
|
|
9a8a360e21 | ||
|
|
cd5364dd36 | ||
|
|
70273798b6 | ||
|
|
baaa780ade | ||
|
|
39751736ca | ||
|
|
1a8bb69288 | ||
|
|
b8c793741f | ||
|
|
0f20983c37 | ||
|
|
9c3b8c7f59 | ||
|
|
943d817e71 | ||
|
|
564b82c427 | ||
|
|
26df915801 | ||
|
|
eefc594ec1 | ||
|
|
6cdfb9db3e | ||
|
|
1ce6cb9419 | ||
|
|
141da4ba36 | ||
|
|
9db7b210bc | ||
|
|
090ffe05f4 | ||
|
|
66f6d0f7ed | ||
|
|
285145fdcb | ||
|
|
37df7fb08c | ||
|
|
5a91fa24e9 | ||
|
|
bdd277d876 | ||
|
|
d7be6e351d | ||
|
|
c9f2af6b96 | ||
|
|
b426041598 | ||
|
|
2d7e138b2f | ||
|
|
332e6c33ac | ||
|
|
99993720f2 | ||
|
|
59a888c861 | ||
|
|
6cccad0e17 | ||
|
|
8ffd0e04cb | ||
|
|
3d6884fe9a | ||
|
|
2a1e26d0c4 | ||
|
|
b842bd935e | ||
|
|
3a47089a00 | ||
|
|
e1f43dc343 | ||
|
|
75b3c23a38 | ||
|
|
7a271190d5 | ||
|
|
c725ad4cc3 | ||
|
|
c07d7ecbb0 | ||
|
|
99ffc82de0 | ||
|
|
7840b48727 | ||
|
|
401baa6653 | ||
|
|
70fc204e70 | ||
|
|
a01d778f65 | ||
|
|
ff3e2caf49 | ||
|
|
95614a383f | ||
|
|
44fc24951f | ||
|
|
19bc802f09 | ||
|
|
ba9f2e01c8 | ||
|
|
8ece0a7eb0 | ||
|
|
d91490874c | ||
|
|
6a3a367cda | ||
|
|
84e6532a80 | ||
|
|
cf36ced2a7 | ||
|
|
c6b65f943a | ||
|
|
6aa1c6b0ea | ||
|
|
2dcaa5270d | ||
|
|
1d44b2792e | ||
|
|
d0577ded59 | ||
|
|
ae5a07c756 | ||
|
|
5d3418a8af | ||
|
|
50353c9e49 | ||
|
|
ea850fae56 | ||
|
|
ba11ac171e | ||
|
|
faace9012a | ||
|
|
90ebfeedc5 | ||
|
|
cfc56c6abf | ||
|
|
d7784e7414 | ||
|
|
7990286e9a | ||
|
|
f7c39eeebb | ||
|
|
4449957fe6 | ||
|
|
5b00b3d859 | ||
|
|
aad568bd28 | ||
|
|
4e1347ad4e | ||
|
|
8073d8325a | ||
|
|
dc1f1fab45 | ||
|
|
dc442ef917 | ||
|
|
796896571d | ||
|
|
cad3f53140 | ||
|
|
b0da5cfa75 | ||
|
|
c2ca979971 | ||
|
|
1e5f288d42 | ||
|
|
b2c94924a2 | ||
|
|
de1797c662 | ||
|
|
db3081efc5 | ||
|
|
dcddd78164 | ||
|
|
335a6864b1 | ||
|
|
1f6dc34ee6 | ||
|
|
fbc9189cba | ||
|
|
586b6f8700 | ||
|
|
1d6013f738 | ||
|
|
c1ef3e3528 | ||
|
|
4b57229396 | ||
|
|
e721babcf7 | ||
|
|
8a816516c9 | ||
|
|
bd09542b34 | ||
|
|
84dad33548 | ||
|
|
588021cf92 | ||
|
|
6a9eccaed0 | ||
|
|
299547842a | ||
|
|
ba2dc61a5d | ||
|
|
2fd2e06bac | ||
|
|
08bfa2772d | ||
|
|
9b9c2e74f6 | ||
|
|
63e49b79cd | ||
|
|
da6ebb8c60 | ||
|
|
b1fbaa7601 | ||
|
|
7327382497 | ||
|
|
cb129238cb | ||
|
|
2ac08a7cf6 | ||
|
|
eeb0870feb | ||
|
|
260d1928d8 | ||
|
|
fa19570c36 | ||
|
|
ed76fcccc8 | ||
|
|
6b4922b825 | ||
|
|
1dc5e7375b | ||
|
|
7628e769be | ||
|
|
7470ca9e3a | ||
|
|
f49cf1f65b | ||
|
|
f21ee5c5ba | ||
|
|
5ea158c21d | ||
|
|
3aa17f0c30 | ||
|
|
e8698f2141 | ||
|
|
9b5946038a | ||
|
|
31a666fe22 | ||
|
|
b9663831fd | ||
|
|
48268111d0 | ||
|
|
af335adc23 | ||
|
|
e88cd0b765 | ||
|
|
a1b6832747 | ||
|
|
7942c367a7 | ||
|
|
f2f683201d | ||
|
|
84dbe5cb69 | ||
|
|
90927c9e8f | ||
|
|
1af688c489 | ||
|
|
1715e7d089 | ||
|
|
4b18ea74a7 | ||
|
|
6c7ab96350 | ||
|
|
b26a261644 | ||
|
|
7ce71497bb | ||
|
|
696b58f9bc | ||
|
|
e45600b4db | ||
|
|
7126a2e295 | ||
|
|
e323d4c692 | ||
|
|
b795ceb33d | ||
|
|
b26265eceb | ||
|
|
2afc9159cf | ||
|
|
8f629b1ef9 | ||
|
|
79a0ec8df9 | ||
|
|
133db009aa | ||
|
|
8f017cda32 | ||
|
|
38546ef074 | ||
|
|
57cc5de60a | ||
|
|
84571682b0 | ||
|
|
d97a3236cf | ||
|
|
1bc1729bdd | ||
|
|
ff94d66163 | ||
|
|
48979d4a75 | ||
|
|
1c4a0fa9c1 | ||
|
|
cf176bccda | ||
|
|
f3cf6bd0b3 | ||
|
|
7a03a00013 | ||
|
|
6382202dbf | ||
|
|
942644dc40 | ||
|
|
eb5bc8a843 | ||
|
|
6a147ff890 | ||
|
|
03670e8773 | ||
|
|
6a64f31940 | ||
|
|
11129f2d70 | ||
|
|
4eb76b6ed5 | ||
|
|
de5449d8b7 | ||
|
|
51e6582d23 | ||
|
|
9726ea1c89 | ||
|
|
28dec3c1f9 | ||
|
|
b28614340c | ||
|
|
4128fbae46 | ||
|
|
51d0715f0f | ||
|
|
9b43063935 | ||
|
|
98d6ef115c | ||
|
|
9113193d0f | ||
|
|
34aa72191b | ||
|
|
cb6e9ddf9c | ||
|
|
6c4cc5c373 | ||
|
|
44125d7471 | ||
|
|
b429ae2158 | ||
|
|
4a71cef3fc | ||
|
|
214d3dfe80 | ||
|
|
b1b8a5f6dc | ||
|
|
6573ba9553 | ||
|
|
de417f5fd1 | ||
|
|
bc065be590 | ||
|
|
05163532fe | ||
|
|
37e2985c46 | ||
|
|
8f21c0c7bf | ||
|
|
6e5b5dd77b | ||
|
|
81ae913be1 | ||
|
|
6c3b15a8f9 | ||
|
|
cae2c5ef71 | ||
|
|
1ac6f418bf | ||
|
|
fe4d17eff3 | ||
|
|
ea1a1fba2c | ||
|
|
b2bd871a7c | ||
|
|
6662514e5f | ||
|
|
f874f2a54c | ||
|
|
502f1683e2 | ||
|
|
4feb4f79a9 | ||
|
|
ca2ce43f2f | ||
|
|
6137139586 | ||
|
|
6ca99789c7 | ||
|
|
d596670fdb | ||
|
|
7fbadad649 | ||
|
|
0edaeb1732 | ||
|
|
3cc32e2738 | ||
|
|
baefd24c38 | ||
|
|
aff4158901 | ||
|
|
18c6514666 | ||
|
|
81787211b8 | ||
|
|
1fb0af10a8 | ||
|
|
a5b5ee1912 | ||
|
|
5c638ec355 | ||
|
|
f5b8346a26 | ||
|
|
90a4696114 | ||
|
|
b370de9ea3 | ||
|
|
fbe6e040ea | ||
|
|
cc341300b4 | ||
|
|
9d09ece8b7 | ||
|
|
061c9ef6db | ||
|
|
fbc656f978 | ||
|
|
465596d22d | ||
|
|
149bcdbe05 | ||
|
|
f4de44bbbb | ||
|
|
dad43e969f | ||
|
|
e3a482614c | ||
|
|
7c8c61472e | ||
|
|
d94813ef78 | ||
|
|
dece385ce2 | ||
|
|
9d3a72bc26 | ||
|
|
2f21b5b5c2 | ||
|
|
75fea391f0 | ||
|
|
eaa992ce16 | ||
|
|
2670c917b7 | ||
|
|
5cf9bd74f4 | ||
|
|
3d945656d6 | ||
|
|
ee9e5d2a90 | ||
|
|
670f897a80 | ||
|
|
d02da12d3f | ||
|
|
e2a18316f6 | ||
|
|
ea0c36474e | ||
|
|
8ce7be5173 | ||
|
|
426278d483 | ||
|
|
ad176a690f | ||
|
|
281713c679 | ||
|
|
5c8c6b8d07 | ||
|
|
d0934d8e6d | ||
|
|
5947aa25fd | ||
|
|
436dd884f0 | ||
|
|
6c7ca7a0a9 | ||
|
|
ceb68012d5 | ||
|
|
0575d5d565 | ||
|
|
0994bd08d8 | ||
|
|
7cf65d07c8 | ||
|
|
a5b2dd39cb | ||
|
|
4ad3c3142a | ||
|
|
880778dd7a | ||
|
|
5f9bc4a94b | ||
|
|
353fa85cba | ||
|
|
9eef6c9497 | ||
|
|
d1850f5e96 | ||
|
|
66d5f7e5b8 | ||
|
|
ea24e2653a | ||
|
|
456350c669 | ||
|
|
46142091d1 | ||
|
|
43741f4d58 | ||
|
|
98082158b7 | ||
|
|
ff69a0f487 | ||
|
|
507e7e507f | ||
|
|
3388701603 | ||
|
|
17bb8407b8 | ||
|
|
2956dabc8b | ||
|
|
214d06927e | ||
|
|
a3cf09287f | ||
|
|
af5354afb0 | ||
|
|
cf8546ebc5 | ||
|
|
f6cb133c4d | ||
|
|
b773f256a4 | ||
|
|
b4e1d00633 | ||
|
|
f3eb2c4cdb | ||
|
|
33a002003e | ||
|
|
a33151da91 | ||
|
|
5dd11aca94 | ||
|
|
29171bce5d | ||
|
|
177e4fdcdc | ||
|
|
1b40f112af | ||
|
|
2bf8e0cfbc | ||
|
|
f89a2f880d | ||
|
|
c6dd27180a | ||
|
|
d99cc7869a | ||
|
|
f6564fa758 | ||
|
|
e4395497ed | ||
|
|
b7656fa951 | ||
|
|
e092c805e8 | ||
|
|
ab7681f3d0 | ||
|
|
0c71f0ebef | ||
|
|
70756befa2 | ||
|
|
025303a01a | ||
|
|
8f74095a85 | ||
|
|
9ea6c18cd2 | ||
|
|
77a0bbe7ee | ||
|
|
66b11134c0 | ||
|
|
47817e06ac | ||
|
|
418d658658 | ||
|
|
b24739cabb | ||
|
|
4adf45094a | ||
|
|
51eaf3a90e | ||
|
|
b76cbc3bc6 | ||
|
|
f54fd5adb7 | ||
|
|
07c9e62205 | ||
|
|
b4a1a4bb19 | ||
|
|
04fb389d59 | ||
|
|
2454f1bbab | ||
|
|
8f1ebb309c | ||
|
|
67562c7297 | ||
|
|
895973e95b | ||
|
|
a6f10032c5 | ||
|
|
83b9b779b4 | ||
|
|
0f3b0ec144 | ||
|
|
9981a28638 | ||
|
|
ee0100895b | ||
|
|
aec47d9d0b | ||
|
|
4a427e23d8 | ||
|
|
7105360b31 | ||
|
|
30a3d87431 | ||
|
|
9d45d3e2b9 | ||
|
|
7ff14f5d96 | ||
|
|
5836849ab9 | ||
|
|
d42f69ae95 | ||
|
|
78350663e1 | ||
|
|
afcb7908d5 | ||
|
|
caf4a07473 | ||
|
|
195b7839d0 | ||
|
|
d79baacc1a | ||
|
|
412aa339d9 | ||
|
|
2664fcab19 | ||
|
|
e3fb5140be | ||
|
|
700fe25046 | ||
|
|
c1f34ae946 | ||
|
|
182da7cd5d | ||
|
|
ed5e8cfe76 | ||
|
|
3aafc445c2 | ||
|
|
3a39de75db | ||
|
|
e0e02544d7 | ||
|
|
8f0223dd07 | ||
|
|
c27c7074f6 | ||
|
|
049cfc35f8 | ||
|
|
801eb5f1aa | ||
|
|
98a065b934 | ||
|
|
06cc5705b5 | ||
|
|
7785706737 | ||
|
|
c83b65f27b | ||
|
|
f7946c4e19 | ||
|
|
c68228381b | ||
|
|
a9c06fd935 | ||
|
|
3ec5a2c199 | ||
|
|
0a735384df | ||
|
|
5c579ee0a2 | ||
|
|
c42e41ca15 | ||
|
|
d3f000fd69 | ||
|
|
61bc581557 | ||
|
|
bea887fff9 | ||
|
|
eef61ef9e2 | ||
|
|
17e1c43ad5 | ||
|
|
3fec08a492 | ||
|
|
e25f48cb14 | ||
|
|
e2d7a9aebd | ||
|
|
11b82fe944 | ||
|
|
88e54ad26a | ||
|
|
408f8c4502 | ||
|
|
0ba55b64bc | ||
|
|
3a5af3fe0d | ||
|
|
822f3bf260 | ||
|
|
7147dbef30 | ||
|
|
ba843b44c4 | ||
|
|
73d006d8da | ||
|
|
57cf5cb89e | ||
|
|
fc570089c2 | ||
|
|
89b4d154ad | ||
|
|
f60aae6b24 | ||
|
|
12e7ec8995 | ||
|
|
d80e9d09c8 | ||
|
|
7862c43752 | ||
|
|
7bfa7ed0a8 | ||
|
|
86b6ad6542 | ||
|
|
727e21480d | ||
|
|
82e1d7684a | ||
|
|
87dd07c45c | ||
|
|
4486050d06 | ||
|
|
a3ba90d1da | ||
|
|
0e10a4cf45 | ||
|
|
bb0db0f2de | ||
|
|
2215dd7dcd | ||
|
|
888dd7fb5f | ||
|
|
636c1892ec | ||
|
|
d954ae78f6 | ||
|
|
13757243a4 | ||
|
|
40d3281fa1 | ||
|
|
bbb49f536d | ||
|
|
6b9a0028c6 | ||
|
|
d7e4bfafda | ||
|
|
e1510e8277 | ||
|
|
f6d8ccf6e7 | ||
|
|
3b52010213 | ||
|
|
c0cced4f5d | ||
|
|
8642757bd8 | ||
|
|
be1bccd2ac | ||
|
|
5d2dc65e08 | ||
|
|
aa010be11d | ||
|
|
492b3b6715 | ||
|
|
1245d58b7e | ||
|
|
b3195d0819 | ||
|
|
aacc779162 | ||
|
|
96377c72ed | ||
|
|
3c27a5baf4 | ||
|
|
2d12875476 | ||
|
|
acc583f995 | ||
|
|
ac60d54950 | ||
|
|
7490b5926f | ||
|
|
8b8353297d | ||
|
|
d455e4b63b | ||
|
|
cc5fa542e1 | ||
|
|
4cb5d1eaae | ||
|
|
0f279b8087 | ||
|
|
1647d3c520 | ||
|
|
3da376e68a | ||
|
|
1f0bc275a0 | ||
|
|
60bf6315a4 | ||
|
|
4b25a1c955 | ||
|
|
16419ca775 | ||
|
|
f390c6ca92 | ||
|
|
d85fc79734 | ||
|
|
b45129f3d0 | ||
|
|
392f14c89d | ||
|
|
1c49306a46 | ||
|
|
30c50968a2 | ||
|
|
3b5ac499cc | ||
|
|
754fdbe284 | ||
|
|
1ce238722a | ||
|
|
0b4383d263 | ||
|
|
3b957142bf | ||
|
|
74b02f6bcf | ||
|
|
2f8d5eca21 | ||
|
|
78d3384d7e | ||
|
|
be74c98963 | ||
|
|
c84e727b22 | ||
|
|
38c84d1e82 | ||
|
|
a0d3b084a3 | ||
|
|
086d463277 | ||
|
|
610bbda43d | ||
|
|
5dc8bbf963 | ||
|
|
93edb489f7 | ||
|
|
c12091c5f0 | ||
|
|
eeaf678485 | ||
|
|
5e71a477de | ||
|
|
fef78cfeed | ||
|
|
df2a873ac6 | ||
|
|
4dd3e550a8 | ||
|
|
a865cb87ef | ||
|
|
52cc9cb579 | ||
|
|
aa16b7cffa | ||
|
|
917f99ff13 | ||
|
|
b70bd85370 | ||
|
|
edd8872403 | ||
|
|
d0a2d6c24c | ||
|
|
0a7ee1c52e | ||
|
|
205a56f086 | ||
|
|
26f5e0b0e5 | ||
|
|
ba66ed6279 | ||
|
|
d9021b0f57 | ||
|
|
44281c2449 | ||
|
|
38590f8278 | ||
|
|
201e3382f4 | ||
|
|
840917d123 | ||
|
|
7d59b18058 | ||
|
|
443c53e294 | ||
|
|
32fe3a664c | ||
|
|
3c5c7e71f1 | ||
|
|
8aaf3bb6e2 | ||
|
|
bf1ded0bd5 | ||
|
|
ca84948751 | ||
|
|
70a03b49da | ||
|
|
c9a89e5dc3 | ||
|
|
693aa1aa93 | ||
|
|
2bcc0beec7 | ||
|
|
caaed9603d | ||
|
|
d2ce84cac4 | ||
|
|
1cdb469d08 | ||
|
|
cdddffbe79 | ||
|
|
4ba7022b54 | ||
|
|
4b27532ac6 |
8
.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
4
.eslintrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
extends: 'react-app',
|
||||
parser: '@babel/eslint-parser'
|
||||
};
|
||||
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
github: ohlookitsderpy
|
||||
patreon: ohlookitsderpy
|
||||
ko_fi: ohlookitsderpy
|
||||
12
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for Mue
|
||||
about: Request things to be added to mue
|
||||
title: "[Feature Request]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
@@ -8,7 +8,13 @@ assignees: ''
|
||||
---
|
||||
|
||||
**Description**
|
||||
A clear and concise description of what you want in this new feature.
|
||||
A clear and concise description of what you want adding to Mue. If it's related to a problem, mention so.
|
||||
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Design**
|
||||
Images or Figma prototypes of what your idea would be like.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Add any other context about the feature request here.
|
||||
17
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: fontsource-lexend-deca
|
||||
versions:
|
||||
- ">= 4.a, < 5"
|
||||
- dependency-name: fontsource-roboto
|
||||
versions:
|
||||
- ">= 4.a, < 5"
|
||||
- dependency-name: react-modal
|
||||
versions:
|
||||
- 3.13.1
|
||||
5
.gitignore
vendored
@@ -6,4 +6,7 @@ build/
|
||||
# Files
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
.eslintcache
|
||||
stats.json
|
||||
yarn.lock
|
||||
*.zip
|
||||
@@ -1,76 +0,0 @@
|
||||
# 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
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018-2020 Mue Tab
|
||||
Copyright (c) 2018-2021 The Mue Authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
157
README.md
@@ -1,53 +1,52 @@
|
||||
<img src="https://raw.githubusercontent.com/mue/branding/master/logo/logo_round.png" align="left" width="180px" height="180px"/>
|
||||
<img src="https://raw.githubusercontent.com/mue/branding/main/logo/logo_round.png" align="left" width="180px" height="180px"/>
|
||||
<img align="left" width="0" height="192px" hspace="10"/>
|
||||
|
||||
> <a href="https://muetab.xyz/">Mue</a>
|
||||
> <a href="https://muetab.com/">Mue</a>
|
||||
|
||||
[](/LICENSE) [](https://discord.gg/zv8C9F8) []()
|
||||
[](/LICENSE) [](https://discord.gg/zv8C9F8) []()
|
||||
<br>
|
||||
[](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja) [](https://addons.mozilla.org/firefox/addon/mue) [](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
|
||||
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customizable tab page to most modern browsers
|
||||
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers.
|
||||
|
||||
<br>
|
||||
|
||||
## Table of contents
|
||||
* [Screenshot](#screenshot)
|
||||
* [Features](#features)
|
||||
* [Planned Features](#planned-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)
|
||||
* [Edge Chromium](#edge-chromium)
|
||||
* [Whale](#whale)
|
||||
* [Other](#other)
|
||||
|
||||
## Screenshot
|
||||

|
||||
* [Contributing](#development)
|
||||
* [Translations](#translations)
|
||||
* [Credits](#credits)
|
||||
* [Developers](#developers)
|
||||
* [Translators](#translators)
|
||||
* [Contributors](#contributors)
|
||||
* [Resources](#resources)
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
* Fast and free
|
||||
* Supports multiple browsers
|
||||
* Actively developed and open source
|
||||
* Automatically updating API (no tracking) with new photos, quotes and offline mode
|
||||
* Search bar
|
||||
* Automatically updating [API](https://github.com/mue/api) with new photos, quotes and offline mode
|
||||
* Widgets such as searchbar, weather, quick links, clock, date, quote, greeting
|
||||
* 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!
|
||||
* Navbar with copy button, favourite background, notes feature etc
|
||||
* [Marketplace](https://github.com/mue/marketplace) - download custom photo packs, quote packs and preset settings made by the community
|
||||
|
||||
### Planned Features
|
||||
Please see our [roadmap](https://github.com/mue/mue/projects/2)
|
||||
Please see our [roadmap](https://github.com/mue/mue/projects)
|
||||
|
||||
## Installation
|
||||
*A demo of the tab can be found [here](https://demo.muetab.xyz)*
|
||||
*A demo of the tab can be found [here](https://demo.muetab.com), and the latest GitHub commit build [here](https://mue.vercel.app)*
|
||||
### Chrome
|
||||
[](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
|
||||
<br>
|
||||
@@ -61,90 +60,36 @@ Please see our [roadmap](https://github.com/mue/mue/projects/2)
|
||||
### Edge (Chromium)
|
||||
[Microsoft Edge Addons](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja)
|
||||
|
||||
### Opera/Other
|
||||
[GitHub Releases](https://github.com/mue/mue/releases)
|
||||
|
||||
### Development
|
||||
#### Requirements
|
||||
<ol>
|
||||
<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> 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/Edge (Chromium)</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<li> <code>yarn run build</code> or <code>npm run build</code>
|
||||
<li> <code>yarn run chrome</code> or <code>npm run chrome</code>
|
||||
<li> Visit <code>chrome://extensions</code> in Chrome
|
||||
<li> Click <b>Load unpacked</b> (Make sure <b>Developer Mode</b> is on)
|
||||
<li> Go to the directory containing the built copy of Mue and click <b>ok</b>
|
||||
<li> Enjoy your new tab!
|
||||
</details>
|
||||
<details>
|
||||
<summary><b>Opera</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<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>
|
||||
<li> Enjoy your new tab!
|
||||
</details>
|
||||
<details>
|
||||
<summary><b>Firefox</b> (Click to expand)</summary>
|
||||
<ol>
|
||||
<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>
|
||||
<li> Enjoy your new tab!
|
||||
</ol>
|
||||
</details>
|
||||
<details>
|
||||
<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>
|
||||
|
||||
## 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>
|
||||
|
||||
### Contributors
|
||||
[Wessel Tip](https://github.com/Wessel) - Development <br>
|
||||
[Isaac (Eartharoid)](https://github.com/eartharoid) - QA, Development, Photographer <br>
|
||||
|
||||
### Translators
|
||||
English - [David Ralph (ohlookitsderpy)](https://github.com/ohlookitsderpy) & [Alex Sparkes](https://github.com/alexsparkes)
|
||||
|
||||
Dutch - [Wessel Tip](https://github.com/Wessel)
|
||||
|
||||
French - [Alex Sparkes](https://github.com/alexsparkes)
|
||||
|
||||
Norwegian - [Anders](https://github.com/FuryingFox)
|
||||
|
||||
Russian - [MrZillaGold](https://github.com/MrZillaGold)
|
||||
### Whale
|
||||
[Whale Store](https://store.whale.naver.com/detail/ecllekeilcmicbfkkiknfdddbogibbnc)
|
||||
|
||||
### Other
|
||||
[Pexels](https://pexels.com) - Stock photos used for offline mode
|
||||
[GitHub Releases](https://github.com/mue/mue/releases)
|
||||
|
||||
[Opera Forum](https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14) - Portions of code to add Opera support
|
||||
## Development
|
||||
Please see the [documentation](https://docs.muetab.com/development#mue-tab).
|
||||
|
||||
[Google Fonts](https://fonts.google.com) - Lexend Deca and Roboto fonts
|
||||
### Translations
|
||||
Please see the [documentation](https://docs.muetab.com/translations).
|
||||
|
||||
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
|
||||
## Credits
|
||||
### Developers
|
||||
[David Ralph](https://github.com/davidjcralph) - Lead development, photographer <br/>
|
||||
[Alex Sparkes](https://github.com/alexsparkes) - Name, lead design, photographer <br/>
|
||||
[Isaac Saunders](https://github.com/eartharoid) - QA, development, photographer <br/>
|
||||
[Wessel Tip](https://github.com/Wessel) - Development <br/>
|
||||
### Translators
|
||||
[Wessel Tip](https://github.com/Wessel), [Heimen Stoffels](https://github.com/Vistaus) - Dutch <br/>
|
||||
[Alex Sparkes](https://github.com/alexsparkes), [Maxime](https://github.com/exiam) - French <br/>
|
||||
[Anders](https://github.com/FuryingFox) - Norwegian <br/>
|
||||
[Pronin Egor](https://github.com/MrZillaGold) - Russian <br/>
|
||||
[Vicente](https://github.com/Vicente015) - Spanish <br/>
|
||||
[Austin Huang](https://github.com/austinhuang0131) - Chinese (Simplified) <br/>
|
||||
[FreeFun](https://github.com/xXFreeFunXx) - German <br/>
|
||||
### Contributors
|
||||
Many thanks to the photographers [here](https://api.muetab.com/images/photographers) for letting us use their wonderful photographs.
|
||||
|
||||
And finally, a big thank you to all the other [contributors](https://github.com/mue/mue/graphs/contributors)!
|
||||
### Resources
|
||||
[Pexels](https://pexels.com), [Unsplash](https://unsplash.com) - Stock photos used for offline mode <br/>
|
||||
[Undraw](https://undraw.co) - Welcome modal images
|
||||
|
||||
|
Before Width: | Height: | Size: 262 KiB |
BIN
assets/screenshot.webp
Normal file
|
After Width: | Height: | Size: 308 KiB |
BIN
assets/screenshot2.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
6
babel.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env', ['@babel/preset-react', {
|
||||
runtime: 'automatic'
|
||||
}]],
|
||||
plugins: ['@babel/plugin-proposal-class-properties', '@babel/transform-runtime', '@babel/plugin-transform-react-inline-elements', 'babel-plugin-transform-react-class-to-function', '@babel/plugin-transform-react-constant-elements']
|
||||
};
|
||||
8
manifest/_locales/de/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en_GB/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/en_US/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/es/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/fr/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/nl/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/no/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/ru/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
8
manifest/_locales/zh_CN/messages.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
10
manifest/background-chrome.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable no-undef */
|
||||
chrome.runtime.setUninstallURL('https://muetab.com/uninstall');
|
||||
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === 'install') {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getURL('index.html')
|
||||
});
|
||||
}
|
||||
});
|
||||
10
manifest/background-firefox.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable no-undef */
|
||||
browser.runtime.setUninstallURL('https://muetab.com/uninstall');
|
||||
|
||||
browser.runtime.onInstalled.addListener((details) => {
|
||||
if (details.reason === 'install') {
|
||||
browser.tabs.create({
|
||||
url: browser.runtime.getURL('index.html')
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
/* 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')
|
||||
});
|
||||
});
|
||||
@@ -1,18 +1,25 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
"manifest_version": 2,
|
||||
"offline_enabled": true,
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"version": "5.3.0",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"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"
|
||||
},
|
||||
"content_security_policy": "script-src 'self' https://api.bing.com https://www.google.com; object-src 'self'",
|
||||
"background": {
|
||||
"persistent": false,
|
||||
"scripts": [ "background-chrome.js" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
"manifest_version": 2,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for modern browsers.",
|
||||
"version": "5.3.0",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"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"
|
||||
},
|
||||
"chrome_settings_overrides": {
|
||||
"homepage": "index.html"
|
||||
},
|
||||
"content_security_policy": "script-src 'self' https://api.bing.com https://www.google.com; object-src 'self'"
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
125
package.json
@@ -1,54 +1,71 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "mue",
|
||||
"private": true,
|
||||
"author": "The Mue Authors (https://github.com/mue/mue/graphs/contributors)",
|
||||
"description": "Fast, open and free-to-use new tab page for modern browsers.",
|
||||
"repository": {
|
||||
"url": "github:mue/mue"
|
||||
},
|
||||
"homepage": "https://muetab.com",
|
||||
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "5.3.0",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@fontsource/lexend-deca": "4.4.5",
|
||||
"@fontsource/montserrat": "4.4.5",
|
||||
"@material-ui/core": "5.0.0-beta.4",
|
||||
"@material-ui/icons": "5.0.0-beta.4",
|
||||
"react": "17.0.2",
|
||||
"react-clock": "3.0.0",
|
||||
"react-color-gradient-picker": "0.1.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-modal": "3.14.3",
|
||||
"react-sortable-hoc": "2.0.0",
|
||||
"react-toastify": "7.0.4",
|
||||
"weather-icons-react": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/eslint-parser": "^7.15.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.14.5",
|
||||
"@babel/plugin-transform-react-inline-elements": "^7.14.5",
|
||||
"@babel/plugin-transform-runtime": "^7.15.0",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@eartharoid/deep-merge": "^0.0.1",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-transform-react-class-to-function": "^1.2.2",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^6.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"mini-css-extract-plugin": "^2.2.0",
|
||||
"sass": "^1.38.0",
|
||||
"sass-loader": "^12.1.0",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"webpack": "^5.51.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack serve",
|
||||
"build": "webpack --mode=production",
|
||||
"chrome": "cp manifest/chrome.json build/manifest.json && cp -r manifest/_locales build/_locales && cp manifest/background-chrome.js build/background-chrome.js",
|
||||
"firefox": "rm -rf build/_locales && cp manifest/firefox.json build/manifest.json && cp manifest/background-firefox.js build/background-firefox.js"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/icons/logo_horizontal.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
@@ -1,10 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 490 B |
1
public/icons/undraw_add_files_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="783" height="702" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path d="m400.51 1.53-25.446 6.562L61.56 88.94 36.113 95.5a48.18 48.18 0 0 0-34.582 58.618l110.341 427.877a48.183 48.183 0 0 0 22.157 29.413 48.183 48.183 0 0 0 36.46 5.17l.066-.017 364.265-93.936.066-.017a48.18 48.18 0 0 0 34.583-58.618L459.128 36.113A48.18 48.18 0 0 0 400.51 1.53z" fill="#F2F2F2"/><path d="m403.969 14.945-30.139 7.773-304.119 78.426-30.139 7.772a34.312 34.312 0 0 0-24.627 41.743l110.341 427.878a34.307 34.307 0 0 0 41.743 24.627l.065-.017L531.36 509.21l.066-.017a34.308 34.308 0 0 0 24.627-41.743L445.712 39.573a34.31 34.31 0 0 0-41.743-24.628z" fill="#fff"/><path d="m381.212 153.503-184.273 47.521a8.016 8.016 0 0 1-9.761-5.759 8.014 8.014 0 0 1 5.759-9.762l184.273-47.52a8.012 8.012 0 0 1 9.754 5.761 8.01 8.01 0 0 1-5.752 9.759zM419.977 171.439l-216.284 55.775a8.01 8.01 0 0 1-9.761-5.759 8.011 8.011 0 0 1 5.759-9.761l216.284-55.775a8.011 8.011 0 0 1 9.761 5.759 8.012 8.012 0 0 1-5.759 9.761zM411.48 270.877l-184.273 47.52a8.014 8.014 0 0 1-4.003-15.52l184.273-47.521a8.014 8.014 0 0 1 4.003 15.521zM450.245 288.813l-216.284 55.775a8.014 8.014 0 0 1-4.003-15.521l216.284-55.775a8.015 8.015 0 0 1 4.003 15.521zM441.748 388.25l-184.273 47.521a8.017 8.017 0 0 1-9.756-5.761 8.011 8.011 0 0 1 5.753-9.76l184.273-47.52a8.014 8.014 0 0 1 4.003 15.52zM480.513 406.186l-216.284 55.775a8 8 0 0 1-6.077-.854 8.019 8.019 0 0 1-3.866-8.027 8.01 8.01 0 0 1 3.121-5.284 8.006 8.006 0 0 1 2.82-1.355l216.284-55.775a8.011 8.011 0 0 1 9.761 5.759 8.012 8.012 0 0 1-5.759 9.761z" fill="#F2F2F2"/><path d="m165.481 249.749-65.212 16.817a3.847 3.847 0 0 1-4.68-2.762l-14.97-58.048a3.845 3.845 0 0 1 2.761-4.681l65.213-16.817a3.85 3.85 0 0 1 4.68 2.762l14.97 58.048a3.846 3.846 0 0 1-2.762 4.681zM195.749 367.122l-65.212 16.817a3.845 3.845 0 0 1-4.681-2.761l-14.97-58.048a3.854 3.854 0 0 1 .413-2.912 3.853 3.853 0 0 1 2.349-1.769l65.212-16.817a3.849 3.849 0 0 1 4.681 2.761l14.969 58.049a3.847 3.847 0 0 1-2.761 4.68zM226.019 484.496l-65.213 16.817a3.846 3.846 0 0 1-4.681-2.762l-14.969-58.048a3.846 3.846 0 0 1 2.761-4.681l65.213-16.817a3.85 3.85 0 0 1 4.681 2.762l14.969 58.048a3.846 3.846 0 0 1-2.761 4.681zM654.658 109.992H278.34a48.179 48.179 0 0 0-48.125 48.125v441.876a48.176 48.176 0 0 0 48.125 48.125h376.318a48.184 48.184 0 0 0 48.125-48.125V158.117a48.179 48.179 0 0 0-48.125-48.125z" fill="#E6E6E6"/><path d="M654.658 123.846H278.339a34.309 34.309 0 0 0-34.271 34.271v441.876a34.312 34.312 0 0 0 34.271 34.27h376.319a34.309 34.309 0 0 0 34.271-34.27V158.117a34.309 34.309 0 0 0-34.271-34.271z" fill="#fff"/><path d="M694.194 701.88c48.519 0 87.85-39.332 87.85-87.85 0-48.519-39.331-87.851-87.85-87.851-48.518 0-87.85 39.332-87.85 87.851 0 48.518 39.332 87.85 87.85 87.85z" fill="#5352ED"/><path d="M598.022 366.656H407.72a8.018 8.018 0 0 1-8.023-8.015 8.021 8.021 0 0 1 2.351-5.67 8.005 8.005 0 0 1 5.672-2.344h190.302a8.016 8.016 0 0 1 0 16.029zM631.08 393.703H407.72a8.017 8.017 0 0 1-7.412-4.945 8.021 8.021 0 0 1 0-6.138 8.008 8.008 0 0 1 4.343-4.338 8.017 8.017 0 0 1 3.069-.607h223.36a8.013 8.013 0 1 1 0 16.028zM598.022 487.869H407.72a8.017 8.017 0 0 1-7.412-4.945 8.021 8.021 0 0 1 0-6.138 8.008 8.008 0 0 1 4.343-4.338 8.017 8.017 0 0 1 3.069-.607h190.302a8.013 8.013 0 1 1 0 16.028zM631.08 514.917H407.72a8.018 8.018 0 0 1-8.023-8.014 8.014 8.014 0 0 1 8.023-8.015h223.36a8.027 8.027 0 0 1 5.673 2.344 8.02 8.02 0 0 1 0 11.341 8.016 8.016 0 0 1-5.673 2.344zM365.093 405.982h-67.346a3.847 3.847 0 0 1-3.843-3.843v-59.947a3.847 3.847 0 0 1 3.843-3.843h67.346a3.847 3.847 0 0 1 3.843 3.843v59.947a3.85 3.85 0 0 1-3.843 3.843zM365.093 527.195h-67.346a3.847 3.847 0 0 1-3.843-3.843v-59.947a3.847 3.847 0 0 1 3.843-3.843h67.346a3.847 3.847 0 0 1 3.843 3.843v59.947a3.847 3.847 0 0 1-3.843 3.843z" fill="#E6E6E6"/><path d="M598.234 231.721H457.932a8.015 8.015 0 1 1 0-16.028h140.302a8.015 8.015 0 1 1 0 16.028zM631.292 258.769h-173.36a8.009 8.009 0 0 1-7.404-4.948 8.005 8.005 0 0 1 0-6.133 8.011 8.011 0 0 1 7.404-4.948h173.36a8.016 8.016 0 0 1 5.667 13.681 8.016 8.016 0 0 1-5.667 2.348z" fill="#CCC"/><path d="M426.882 291.547H297.536a3.845 3.845 0 0 1-3.843-3.843V186.757a3.847 3.847 0 0 1 3.843-3.843h129.346a3.847 3.847 0 0 1 3.843 3.843v100.947a3.843 3.843 0 0 1-1.127 2.716 3.843 3.843 0 0 1-2.716 1.127z" fill="#5352ED"/><path d="M700.5 648v-57a6.5 6.5 0 1 0-13 0v57a6.5 6.5 0 1 0 13 0z" fill="#fff" stroke="#fff" stroke-width="5"/><path d="m728.202 621.069.609-.571a6.5 6.5 0 0 0 .33-9.154l-30.362-32.861a6.5 6.5 0 0 0-9.548-.001l-30.371 32.862a6.5 6.5 0 0 0 .329 9.154l.61.571a6.498 6.498 0 0 0 9.219-.332l23.885-25.853a1.5 1.5 0 0 1 2.204 0l23.875 25.852a6.5 6.5 0 0 0 9.22.333z" fill="#fff" stroke="#fff" stroke-width="5"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h782.044v701.88H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
1
public/icons/undraw_around_the_world_modified.svg
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
1
public/icons/undraw_by_the_road.svg
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 36 KiB |
1
public/icons/undraw_dark_mode.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="769.001" height="771.636"><path d="M769.001 0H310.286a400.77 400.77 0 0 0-5.285 65c0 219.81 178.191 398 398 398a400.7 400.7 0 0 0 66-5.45z" fill="#2f2e41"/><path d="M574.85 248.809a57.4 57.4 0 0 1-20.163-3.64 3 3 0 1 1 2.106-5.617 52.393 52.393 0 0 0 51.263-8.531c-22.038.234-43.595-10.18-54.32-26.893-6.894-10.743-9.148-23.384-6.52-36.558a65.225 65.225 0 0 1 12.09-26.277 52.051 52.051 0 0 0-36.024 54.65 3 3 0 1 1-5.971.592 58.044 58.044 0 0 1 49.792-63.138 3 3 0 0 1 2.55 5.076C554.31 154.06 545 179.402 558.787 200.888c10.878 16.95 34.283 26.644 56.928 23.58a3 3 0 0 1 2.642 4.968 58.387 58.387 0 0 1-43.507 19.373z" fill="#6c63ff"/><path d="M527.001 250a22 22 0 1 1 22-22 22.025 22.025 0 0 1-22 22zm0-38a16 16 0 1 0 16 16 16.018 16.018 0 0 0-16-16z" fill="#6c63ff"/><path d="M8.69 567.078A10.276 10.276 0 0 1 .42 554.027l7.788-26.48 11.835 2.218.509 26.967a10.276 10.276 0 0 1-11.86 10.346z" fill="#9f616a"/><path fill="#2f2e41" d="m76.996 736.132-14.793-2.219 3.698-144.235-20.71 62.132-11.095 87.281-14.054-3.698-2.959-90.979 16.273-107.252 81.363 18.492-37.723 180.478z"/><path d="M70.205 771.636a10.873 10.873 0 0 1-10.873-10.873q0-.533.052-1.064l2.297-23.354a3.428 3.428 0 0 1 1.784-2.678c4.36-2.352 8.845-1.782 13.434 1.288a3.407 3.407 0 0 1 1.48 2.429l2.623 22.098a10.873 10.873 0 0 1-10.797 12.154zM9.82 769.898a9.794 9.794 0 0 1-4.754-14.662l15.78-23.457c5.689-4.082 9.12-2.094 10.454 5.534l3.19-7.993 2.404 2.622a7.647 7.647 0 0 1 .404 9.854l-16.368 24.91a9.794 9.794 0 0 1-11.11 3.192z" fill="#2f2e41"/><circle cx="109.542" cy="375.915" r="22.19" fill="#9f616a"/><path d="M123.595 416.596 85.872 409.2c4.86-10.364 8.75-13.87 4.438-25.15h30.327c-2.675 11.683-.09 22.648 2.958 32.546z" fill="#9f616a"/><path d="m119.157 566.009-88.76-25.149c17.78-39.13 21.503-82.09 16.232-127.504a8.217 8.217 0 0 1 7.207-9.116q.15-.017.3-.03c11.578-.953 23.648-2.177 36.174-3.886l14.054 9.615 16.273-3.698c5.221 2.53 10.48 4.636 15.227 6.734a19.038 19.038 0 0 1 10.35 23.414c-14.462 43.677-24.143 86.94-27.057 129.62z" fill="#e6e6e6"/><path d="m22.261 532.724-17.012-2.22L45.55 414.38c2.05-5.907 7.134-9.104 13.323-9.988l5.918.74-4.438 68.788zM155.525 510.524a13.484 13.484 0 0 1-13.487-8.271l-16.224-39.058 13.314-48.078 3.52 2.24q.509.323 1.004.663a31.653 31.653 0 0 1 13.002 31.471l-3.472 20.361 14.323 30.753a13.484 13.484 0 0 1-11.98 9.92z" fill="#e6e6e6"/><path d="M111.248 343.027a23.564 23.564 0 0 1 10.766 3.17c.755.439.406 2.022 1.103 2.539.878.65 2.785.255 3.563 1.017a23.615 23.615 0 0 1 7.022 19.026l-1.238 12.311-2.921-3.194a30.448 30.448 0 0 0-20.305-9.97q-.497-.033-.996-.052l2.247-3.933-3.905 3.906a38.03 38.03 0 0 0-5.321.433l2.988-5.23-5.734 5.734a15.331 15.331 0 0 0-10.713 8.738l-.637 1.411-.713-11.745a23.674 23.674 0 0 1 23.154-24.182q.82-.018 1.64.02z" fill="#2f2e41"/><ellipse cx="131.877" cy="380.205" rx="1.782" ry="4.233" fill="#9f616a"/><ellipse cx="87.319" cy="378.868" rx="1.782" ry="4.233" fill="#9f616a"/><path d="M174.034 435.42a10.276 10.276 0 0 1 7.134 13.706L171.16 474.85l-11.604-3.216 1.782-26.913a10.276 10.276 0 0 1 12.696-9.302z" fill="#9f616a"/><path fill="#e6e6e6" d="m160.579 509.055-18.492-11.835 15.533-31.806 19.231 7.397-9.615 28.107-6.657 8.137z"/><path d="M173.001 409.101V284h-2v125.101a5 5 0 1 0 2 0zm-1 7.899a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3z" fill="#3f3d56"/><path d="M170.435 164.57a4.314 4.314 0 0 1-4.314-4.315v-12.941a4.314 4.314 0 0 1 8.627 0v12.941a4.314 4.314 0 0 1-4.313 4.314zM170.435 263.787a4.314 4.314 0 0 1-4.314-4.313v-12.942a4.314 4.314 0 1 1 8.627 0v12.942a4.314 4.314 0 0 1-4.313 4.313zM200.939 177.203a4.314 4.314 0 0 1-3.05-7.364l9.15-9.15a4.314 4.314 0 0 1 6.1 6.1l-9.15 9.15a4.3 4.3 0 0 1-3.05 1.264zM130.78 247.362a4.314 4.314 0 0 1-3.05-7.364l9.15-9.15a4.314 4.314 0 0 1 6.1 6.1l-9.15 9.151a4.3 4.3 0 0 1-3.05 1.263zM226.514 207.708h-12.941a4.314 4.314 0 0 1 0-8.628h12.941a4.314 4.314 0 1 1 0 8.628zM127.296 207.708h-12.941a4.314 4.314 0 1 1 0-8.628h12.941a4.314 4.314 0 0 1 0 8.628zM210.09 247.362a4.3 4.3 0 0 1-3.05-1.263l-9.152-9.15a4.314 4.314 0 1 1 6.101-6.101l9.15 9.15a4.314 4.314 0 0 1-3.05 7.365zM139.93 177.203a4.3 4.3 0 0 1-3.05-1.263l-9.15-9.15a4.314 4.314 0 0 1 6.1-6.102l9.15 9.151a4.314 4.314 0 0 1-3.05 7.364zM170.435 229.277a25.883 25.883 0 1 1 25.883-25.883 25.912 25.912 0 0 1-25.883 25.883zm0-43.139a17.255 17.255 0 1 0 17.255 17.256 17.275 17.275 0 0 0-17.255-17.256z" fill="#6c63ff"/></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
1
public/icons/undraw_on_the_way.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
1
public/icons/undraw_private_data_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="400" height="100" viewBox="0 0 629 160" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M502.973 139.536h68.977L606.438 79.8 571.95 20.064h-68.977L468.484 79.8l34.489 59.736zM56.973 138.577h68.977l34.488-59.736-34.488-59.735H56.973L22.484 78.84l34.489 59.736zM279.973 139.616h68.977l34.488-59.736-34.488-59.735h-68.977L245.484 79.88l34.489 59.736z" fill="#F2F2F2"/><path d="M357.693 159.759h-86.464a5.019 5.019 0 0 1-4.33-2.5l-43.231-74.88a5.009 5.009 0 0 1 0-5L266.899 2.5a5.02 5.02 0 0 1 4.33-2.5h86.464a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.018 5.018 0 0 1-4.33 2.5zM271.229 2a3.008 3.008 0 0 0-2.598 1.5L225.4 78.38a3.003 3.003 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.598 1.5h86.464a3.012 3.012 0 0 0 2.597-1.5l43.232-74.88a3.003 3.003 0 0 0 0-3L360.29 3.5a2.999 2.999 0 0 0-2.597-1.5h-86.464zM134.693 159.759H48.229a5.018 5.018 0 0 1-4.33-2.5L.668 82.379a5.015 5.015 0 0 1 0-5L43.899 2.5A5.014 5.014 0 0 1 48.23 0h86.464a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.018 5.018 0 0 1-4.33 2.5zM48.229 2a3.008 3.008 0 0 0-2.598 1.5L2.4 78.379a3.009 3.009 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.598 1.5h86.464a3.012 3.012 0 0 0 2.597-1.5l43.232-74.88a3.003 3.003 0 0 0 0-3L137.29 3.5a2.999 2.999 0 0 0-2.597-1.5H48.229zM580.693 159.759H494.23a5.013 5.013 0 0 1-4.331-2.5l-43.231-74.88a5.009 5.009 0 0 1 0-5L489.899 2.5A5.016 5.016 0 0 1 494.23 0h86.463a5.015 5.015 0 0 1 4.33 2.5l43.231 74.879a5.009 5.009 0 0 1 0 5l-43.231 74.88a5.016 5.016 0 0 1-4.33 2.5zM494.23 2a3.01 3.01 0 0 0-2.599 1.5L448.4 78.379a3.003 3.003 0 0 0 0 3l43.231 74.88a3.011 3.011 0 0 0 2.599 1.5h86.463a3.012 3.012 0 0 0 2.598-1.5l43.231-74.88a3.003 3.003 0 0 0 0-3L583.291 3.5a3.009 3.009 0 0 0-2.598-1.5H494.23z" fill="#CCC"/><path d="M91.461 45.863a32.977 32.977 0 1 0 0 65.954 32.977 32.977 0 0 0 0-65.954zm0 9.893a9.893 9.893 0 1 1 0 19.786 9.893 9.893 0 0 1 0-19.786zm0 47.626a24.026 24.026 0 0 1-19.786-10.559c.159-6.595 13.19-10.226 19.786-10.226 6.595 0 19.628 3.631 19.786 10.226a24.062 24.062 0 0 1-19.786 10.559zM314.397 56.907a17.995 17.995 0 0 0-12.604-5.58 18.005 18.005 0 0 0-12.851 31.035l26.067 26.068 25.456-25.456a18.44 18.44 0 0 0-.306-25.761 18.44 18.44 0 0 0-25.762-.306zM558.697 59.821c0 11.728-21.235 37.474-21.235 37.474s-21.235-25.746-21.235-37.474a21.223 21.223 0 0 1 6.207-15.02 21.236 21.236 0 0 1 36.263 14.985v.035z" fill="#5352ED"/><path d="M537.462 68.163a8.342 8.342 0 1 0 0-16.684 8.342 8.342 0 0 0 0 16.684z" fill="#fff"/><path d="M536.702 121.029a8.342 8.342 0 1 0 0-16.685 8.342 8.342 0 0 0 0 16.685z" fill="#5352ED"/></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
1
public/icons/undraw_upgrade_modified.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="1066" height="774" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path opacity=".1" d="M995.19 369.87c.59-6.77.9-13.65.9-20.6 0-108.83-73.84-197.06-164.92-197.06-29 0-56.34 9-80 24.75-64.47-50-148.64-80.21-240.72-80.21-149.83 0-278.71 80.07-335.89 194.9a138.6 138.6 0 0 0-9.6-.34C73.83 291.31 0 379.53 0 488.36c0 40 10 77.23 27.14 108.31 0 97.91 78.45 177.31 175.22 177.31h122l.54-.93c7.17-11.64 24.34-14.55 36.3-7.94 3.16 1.74 6.1 4 9.63 4.8 14.13 3 23.62-19.73 37.92-17.52 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 7.21-2.74 14.52 1.06 19.57 7.23 2.93-8.43 2.91-18.42 7.65-26.15 7.17-11.64 24.34-14.55 36.3-7.94 3.16 1.74 6.1 4 9.63 4.8 14.13 3 23.62-19.73 37.92-17.52 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 11.22-4.27 22.71 7.32 25.47 19a81.066 81.066 0 0 1 1.69 15.64c9.68-4.69 17.86-17.88 28.91-16.17 7.18 1.11 12.78 8.53 20 7.77 4.79-.51 8.42-4.52 12.92-6.22 11.22-4.27 22.71 7.32 25.47 19 1.47 6.2 1.57 12.64 1.82 19h202.15c76 0 137.58-62.32 137.58-139.2 15.42-30 24.33-65.28 24.33-103.06 0-67-28-126.2-70.81-161.8z" fill="#6C63FF"/><path d="M114 582.98v-24.3l11.2 11.2 2.8-2.9-16-16-16 16 2.8 2.8 11.2-11.1v24.3h4zM789 299a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM224 283a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM401 435a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM925 534a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM700 617a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM315 687a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM96 444a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM125 380a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM957 492a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM770 716a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM215 612a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM284 337a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM386.5 227.5H371V212h-2v15.5h-15.5v2H369V245h2v-15.5h15.5v-2zM764 513.89h-9.39v-9.39h-1.22v9.39H744v1.22h9.39v9.39h1.22v-9.39H764v-1.22zM707 688.89h-9.39v-9.39h-1.22v9.39H687v1.22h9.39v9.39h1.22v-9.39H707v-1.22zM240 666.89h-9.39v-9.39h-1.22v9.39H220v1.22h9.39v9.39h1.22v-9.39H240v-1.22zM174 490.89h-9.39v-9.39h-1.22v9.39H154v1.22h9.39v9.39h1.22v-9.39H174v-1.22zM940 667v-24.3l11.2 11.2 2.8-2.9-16-16-16 16 2.8 2.8 11.2-11.1V667h4zM747 385a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM816 618a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM807 439a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM909.5 329.5H894V314h-2v15.5h-15.5v2H892V347h2v-15.5h15.5v-2zM697 592.89h-9.39v-9.39h-1.22v9.39H677v1.22h9.39v9.39h1.22v-9.39H697v-1.22z" fill="#fff"/><path d="m258.343 592.735 3.913-4.048-2.432-2.348-3.913 4.048-15.794 8.254 10.535 10.166 7.691-16.072z" fill="#5352ED"/><path d="m268.811 603.045 4.009-4.15-2.49-2.417-4.016 4.158-16.198 8.463 10.812 10.428 7.883-16.482z" fill="#5352ED"/><path d="m279.414 613.083 3.913-4.048-2.432-2.348-3.913 4.048-15.794 8.254 10.543 10.173 7.683-16.079zM354.436 685.727l4.016-4.158-2.497-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.883-16.481zM365.242 696.161l4.016-4.157-2.498-2.41-4.016 4.158-16.191 8.456 10.805 10.434 7.884-16.481zM376.047 706.596l4.016-4.158-2.498-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.884-16.481zM305.384 638.366l4.016-4.158-2.497-2.409-4.016 4.157-16.191 8.456 10.805 10.435 7.883-16.481z" fill="#5352ED"/><path d="m316.19 648.8 4.016-4.157-2.49-2.403-4.016 4.158-16.199 8.449 10.805 10.435 7.884-16.482zM326.995 659.235l4.016-4.158-2.49-2.402-4.016 4.157-16.199 8.45 10.812 10.441 7.877-16.488zM575.105 337.375c13.499-14.624 20.646-56.373 11.545-65.16-9.102-8.787-49.749.608-63.888 14.613l-.437-.411-277.74 287.63 52.795 50.972 277.733-287.623-.008-.021zM671.535 430.49c13.499-14.623 20.646-56.373 11.545-65.16-9.102-8.787-49.749.609-63.881 14.621l-.436-.412L341.022 667.17l52.795 50.971L671.55 430.518l-.015-.028z" fill="#5352ED"/><path d="M752.018 249.005c19.93-21.3 38.411-74.794 29.317-83.574-9.094-8.78-61.104 12.332-81.634 32.984l-.429-.405-407.254 421.748 52.781 50.972 407.26-421.755-.041.03z" fill="#363192"/><g opacity=".1" fill="#000"><path opacity=".1" d="M574.87 274.92c7.59-17.71 22.41-36.18 31.63-36.07 12 .14 33.62 31.79 36.07 52l.26-21.71c-.21-19.9-23.65-55.18-36.3-55.33-9.1-.11-23.64 17.88-31.32 35.38l-.34 25.73zM435.41 291.7h.6c.67-19.89 23.81-54.61 36.45-54.46 8.26.1 21.11 15.17 29.1 31.16l1.94-162.36h.59c.78-29.16 24.13-80.2 36.77-80 11.94.14 33.36 46.21 35.78 75.9l.23-20c-.11-29.17-23.36-80.77-36-80.92-12.64-.15-36 50.89-36.77 80.05h-.59l-1.94 162.36c-8-16-20.84-31.06-29.1-31.16-12.64-.15-35.78 34.57-36.45 54.46h-.6l-4.77 399.81h.3l4.46-374.84z"/></g><path d="M540.858 25.386h-.01a6.93 6.93 0 0 0-7.012 6.847l-.192 16.189a6.93 6.93 0 0 0 6.847 7.011h.01a6.93 6.93 0 0 0 7.012-6.847l.192-16.189a6.93 6.93 0 0 0-6.847-7.011z" fill="#ECECF3"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h1066v773.96H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -9,8 +9,26 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to use Mue.</noscript>
|
||||
<noscript>
|
||||
<style>
|
||||
*, a {
|
||||
font-family: 'Lexend Deca', sans-serif;
|
||||
text-align: center;
|
||||
color: black;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
*, a {
|
||||
color: white;
|
||||
background: #2f3640 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h1>Error</h1>
|
||||
<h2>You need to enable JavaScript to use Mue</h2>
|
||||
<p>Having trouble? Contact us: <a href='https://muetab.com/contact'>https://muetab.com/contact</a></p>
|
||||
</noscript>
|
||||
<div id='root'></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
|
Before Width: | Height: | Size: 128 KiB |
BIN
public/offline-images/1.webp
Normal file
|
After Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 200 KiB |
BIN
public/offline-images/10.webp
Normal file
|
After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 146 KiB |
BIN
public/offline-images/11.webp
Normal file
|
After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 230 KiB |
BIN
public/offline-images/12.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 737 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 390 KiB |
|
Before Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 94 KiB |
BIN
public/offline-images/2.webp
Normal file
|
After Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 148 KiB |
BIN
public/offline-images/3.webp
Normal file
|
After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 290 KiB |
BIN
public/offline-images/4.webp
Normal file
|
After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 324 KiB |
BIN
public/offline-images/5.webp
Normal file
|
After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 264 KiB |
BIN
public/offline-images/6.webp
Normal file
|
After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 495 KiB |
BIN
public/offline-images/7.webp
Normal file
|
After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 472 KiB |
BIN
public/offline-images/8.webp
Normal file
|
After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 280 KiB |
BIN
public/offline-images/9.webp
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
public/welcome-images/example1.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/welcome-images/example2.webp
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
public/welcome-images/example3.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/welcome-images/example4.webp
Normal file
|
After Width: | Height: | Size: 45 KiB |
12
scripts/updatetranslations.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const fs = require('fs');
|
||||
const merge = require('@eartharoid/deep-merge');
|
||||
|
||||
fs.readdirSync('../src/translations').forEach((file) => {
|
||||
if (file === 'en_GB.json') {
|
||||
return;
|
||||
}
|
||||
|
||||
const newdata = merge(require('../src/translations/en_GB.json'), require('../src/translations/' + file));
|
||||
fs.writeFileSync('../src/translations/' + file, JSON.stringify(newdata, null, 2));
|
||||
fs.appendFileSync('../src/translations/' + file, '\n');
|
||||
});
|
||||
145
src/App.jsx
@@ -1,126 +1,45 @@
|
||||
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 { PureComponent } from 'react';
|
||||
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>;
|
||||
import Background from './components/widgets/background/Background';
|
||||
import Widgets from './components/widgets/Widgets';
|
||||
import Modals from './components/modals/Modals';
|
||||
|
||||
export default class App extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
import { loadSettings, moveSettings } from './modules/helpers/settings';
|
||||
|
||||
this.state = {
|
||||
settingsModal: false,
|
||||
updateModal: false,
|
||||
marketplaceModal: false,
|
||||
addonsModal: false,
|
||||
quickAccessmodal: false,
|
||||
welcomeModal: false
|
||||
};
|
||||
import EventBus from './modules/helpers/eventbus';
|
||||
|
||||
export default class App extends PureComponent {
|
||||
componentDidMount() {
|
||||
// 4.0 -> 5.0 (the key below is only on 5.0)
|
||||
// now featuring 5.0 -> 5.1
|
||||
// the firstRun check was moved here because the old function was useless
|
||||
if (!localStorage.getItem('firstRun') || !localStorage.getItem('stats')) {
|
||||
moveSettings();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'other') {
|
||||
loadSettings(true);
|
||||
}
|
||||
});
|
||||
|
||||
window.stats.tabLoad();
|
||||
}
|
||||
|
||||
// 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 />
|
||||
<>
|
||||
{(localStorage.getItem('background') === 'true') ? <Background/> : null}
|
||||
<ToastContainer position='bottom-right' autoClose={localStorage.getItem('toastDisplayTime') || 2500} newestOnTop={true} closeOnClick 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>
|
||||
<Widgets/>
|
||||
<Modals/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
86
src/components/helpers/autocomplete/Autocomplete.jsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EventBus from '../../../modules/helpers/eventbus';
|
||||
|
||||
import './autocomplete.scss';
|
||||
|
||||
export default class Autocomplete extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filtered: [],
|
||||
input: '',
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
};
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
if (this.state.autocompleteDisabled) {
|
||||
return this.setState({
|
||||
input: e.target.value
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
filtered: this.props.suggestions.filter((suggestion) => suggestion.toLowerCase().indexOf(e.target.value.toLowerCase()) > -1),
|
||||
input: e.target.value
|
||||
});
|
||||
|
||||
this.props.onChange(e.target.value);
|
||||
};
|
||||
|
||||
onClick = (e) => {
|
||||
this.setState({
|
||||
filtered: [],
|
||||
input: e.target.innerText
|
||||
});
|
||||
|
||||
this.props.onClick(e);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
EventBus.on('refresh', (data) => {
|
||||
if (data === 'search') {
|
||||
this.setState({
|
||||
autocompleteDisabled: (localStorage.getItem('autocomplete') !== 'true')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnount() {
|
||||
EventBus.off('refresh');
|
||||
}
|
||||
|
||||
render() {
|
||||
let autocomplete = null;
|
||||
|
||||
// length will only be > 0 if enabled
|
||||
if (this.state.filtered.length > 0 && this.state.input.length > 0) {
|
||||
autocomplete = (
|
||||
<ul className='suggestions'>
|
||||
{this.state.filtered.map((suggestion) => {
|
||||
return (
|
||||
<li key={suggestion} onClick={this.onClick}>
|
||||
{suggestion}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type='text'
|
||||
onChange={this.onChange}
|
||||
value={this.state.input}
|
||||
placeholder={this.props.placeholder || ''}
|
||||
autoComplete='off'
|
||||
id={this.props.id || ''} />
|
||||
{autocomplete}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
50
src/components/helpers/autocomplete/autocomplete.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
.suggestions {
|
||||
text-align: left;
|
||||
font-size: calc(5px + 1.2vmin);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
color: black;
|
||||
border-top-width: 0;
|
||||
list-style: none;
|
||||
margin-top: 40px;
|
||||
max-height: 143px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: 40px;
|
||||
border-radius: 24px;
|
||||
width: 430px;
|
||||
opacity: 0;
|
||||
|
||||
li {
|
||||
padding: 0.5rem;
|
||||
padding-left: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
input[type=text]:focus+.suggestions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1400px) {
|
||||
.suggestions {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .suggestions {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
|
||||
li {
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/components/helpers/tooltip/Tooltip.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import './tooltip.scss';
|
||||
|
||||
export default function Tooltip(props) {
|
||||
return (
|
||||
<div className='tooltip'>
|
||||
{props.children}
|
||||
<span className='tooltipTitle'>{props.title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
src/components/helpers/tooltip/tooltip.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
// todo: possibly add tooltip placement option
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
.tooltipTitle {
|
||||
min-width: 60px;
|
||||
background-color: rgba(255, 255, 255, 0.89);
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
font-size: 0.6rem;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -30px;
|
||||
visibility: hidden;
|
||||
cursor: initial;
|
||||
user-select: none;
|
||||
opacity: 0;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.tooltipTitle {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark .tooltipTitle {
|
||||
background-color: rgba(0, 0, 0, 0.79);
|
||||
color: #ffffff;
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
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>;
|
||||
}
|
||||
}
|
||||
37
src/components/modals/ErrorBoundary.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { ErrorOutline } from '@material-ui/icons';
|
||||
|
||||
export default class ErrorBoundary extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: false
|
||||
};
|
||||
this.language = window.language.modals.main.error_boundary;
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
console.log(error);
|
||||
window.stats.postEvent('modal', 'Error occurred');
|
||||
return {
|
||||
error: true
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<ErrorOutline/>
|
||||
<h1>{this.language.title}</h1>
|
||||
<p>{this.language.message}</p>
|
||||
<button className='refresh' onClick={() => window.location.reload()}>{this.language.refresh}</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
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>;
|
||||
}
|
||||
}
|
||||
75
src/components/modals/Modals.jsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { PureComponent, Suspense, lazy } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Main from './main/Main';
|
||||
import Feedback from './feedback/Feedback';
|
||||
import Navbar from '../widgets/navbar/Navbar';
|
||||
|
||||
import EventBus from '../../modules/helpers/eventbus';
|
||||
|
||||
// Welcome modal is lazy loaded as the user won't use it every time they open a tab
|
||||
// We used to lazy load the main and feedback modals, but doing so broke the modal open animation on first click
|
||||
const Welcome = lazy(() => import('./welcome/Welcome'));
|
||||
const renderLoader = () => <></>;
|
||||
|
||||
export default class Modals extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
mainModal: false,
|
||||
updateModal: false,
|
||||
welcomeModal: false,
|
||||
feedbackModal: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (localStorage.getItem('showWelcome') === 'true' && window.location.search !== '?nointro=true') {
|
||||
this.setState({
|
||||
welcomeModal: true
|
||||
});
|
||||
window.stats.postEvent('modal', 'Opened welcome');
|
||||
}
|
||||
|
||||
// hide refresh reminder once the user has refreshed the page
|
||||
localStorage.setItem('showReminder', false);
|
||||
}
|
||||
|
||||
closeWelcome() {
|
||||
localStorage.setItem('showWelcome', false);
|
||||
this.setState({
|
||||
welcomeModal: false
|
||||
});
|
||||
EventBus.dispatch('refresh', 'widgets');
|
||||
EventBus.dispatch('refresh', 'backgroundwelcome');
|
||||
}
|
||||
|
||||
toggleModal(type, action) {
|
||||
this.setState({
|
||||
[type]: action
|
||||
});
|
||||
|
||||
if (action !== false) {
|
||||
window.stats.postEvent('modal', `Opened ${type.replace('Modal', '')}`);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
{this.state.welcomeModal === false ? <Navbar openModal={(modal) => this.toggleModal(modal, true)}/> : null}
|
||||
<Modal closeTimeoutMS={300} id='modal' onRequestClose={() => this.toggleModal('mainModal', false)} isOpen={this.state.mainModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Main modalClose={() => this.toggleModal('mainModal', false)}/>
|
||||
</Modal>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Modal closeTimeoutMS={300} onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className='Modal welcomemodal mainModal' overlayClassName='Overlay welcomeoverlay' shouldCloseOnOverlayClick={false} ariaHideApp={false}>
|
||||
<Welcome modalClose={() => this.closeWelcome()}/>
|
||||
</Modal>
|
||||
<Modal closeTimeoutMS={300} onRequestClose={() => this.toggleModal('feedbackModal', false)} isOpen={this.state.feedbackModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
|
||||
<Feedback modalClose={() => this.toggleModal('feedbackModal', false)}/>
|
||||
</Modal>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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>;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
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>;
|
||||
}
|
||||
}
|
||||
93
src/components/modals/feedback/Feedback.jsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import './feedback.scss';
|
||||
|
||||
export default class FeedbackModal extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
questionone: 5,
|
||||
questionthree: 5,
|
||||
questiontwoerror: '',
|
||||
questionfourerror: '',
|
||||
formsubmit: ''
|
||||
};
|
||||
this.language = window.language.modals.feedback;
|
||||
}
|
||||
|
||||
async submitForm () {
|
||||
let questiontwoerror, questionfourerror;
|
||||
|
||||
if (document.getElementById('questiontwo').value.length <= 0) {
|
||||
questiontwoerror = this.language.not_filled;
|
||||
}
|
||||
|
||||
if (document.getElementById('questionfour').value.length <= 0) {
|
||||
questionfourerror = this.language.not_filled;
|
||||
}
|
||||
|
||||
if (questiontwoerror || questionfourerror) {
|
||||
this.setState({
|
||||
questiontwoerror: questiontwoerror,
|
||||
questionfourerror: questionfourerror
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
questiontwoerror: '',
|
||||
questionfourerror: ''
|
||||
});
|
||||
|
||||
await fetch(window.constants.FEEDBACK_FORM, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
this.setState({
|
||||
formsubmit: this.language.success
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.props.modalClose();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='feedback'>
|
||||
<h1>{this.language.title}</h1>
|
||||
<span className='closeModal' onClick={this.props.modalClose}>×</span>
|
||||
<>
|
||||
<input type='hidden' name='version' value={window.constants.VERSION} />
|
||||
<>
|
||||
<label>{this.language.question_one}</label>
|
||||
<br/><br/>
|
||||
<label className='values'>0</label>
|
||||
<input className='range' type='range' min='0' max='10' name='question1' value={this.state.questionone} onChange={(e) => this.setState({ questionone: e.target.value })}/>
|
||||
<label className='values'>10 ({this.state.questionone})</label>
|
||||
</>
|
||||
<br/><br/>
|
||||
<>
|
||||
<label>{this.language.question_two}</label>
|
||||
<textarea name='question2' id='questiontwo'/>
|
||||
<p className='feedbackerror'>{this.state.questiontwoerror}</p>
|
||||
</>
|
||||
<>
|
||||
<label>{this.language.question_three}</label>
|
||||
<br/><br/>
|
||||
<label className='values'>0</label>
|
||||
<input className='range' type='range' min='0' max='10' name='question3' value={this.state.questionthree} onChange={(e) => this.setState({ questionthree: e.target.value })}/>
|
||||
<label className='values'>10 ({this.state.questionthree})</label>
|
||||
</>
|
||||
<br/><br/>
|
||||
<>
|
||||
<label>{this.language.question_four}</label>
|
||||
<textarea name='question4' id='questionfour'/>
|
||||
<p className='feedbackerror'>{this.state.questionfourerror}</p>
|
||||
</>
|
||||
<p>{this.state.formsubmit}</p>
|
||||
<button onClick={() => this.submitForm()}>{this.language.submit}</button>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
68
src/components/modals/feedback/feedback.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
@import '../main/scss/index.scss';
|
||||
|
||||
#feedbackmodal {
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 400px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
width: 400px;
|
||||
padding: 5px;
|
||||
|
||||
h1,
|
||||
.closeModal {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 6em;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 50%;
|
||||
border-radius: 48px;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 15px 20px;
|
||||
font-size: 1.5em;
|
||||
background: #5352ed;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(83, 82, 237, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
label.values {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
background-color: var(--sidebar) !important;
|
||||
}
|
||||
|
||||
.feedbackerror {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
50
src/components/modals/main/Main.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Suspense, lazy } from 'react';
|
||||
|
||||
import Tabs from './tabs/backend/Tabs';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
// Lazy load all the tabs instead of the modal itself
|
||||
const Settings = lazy(() => import('./tabs/Settings'));
|
||||
const Addons = lazy(() => import('./tabs/Addons'));
|
||||
const Marketplace = lazy(() => import('./tabs/Marketplace'));
|
||||
|
||||
const renderLoader = () => (
|
||||
<Tabs>
|
||||
<div label={window.language.modals.main.loading}>
|
||||
<div className='emptyitems'>
|
||||
<div className='emptyMessage'>
|
||||
<h1>{window.language.modals.main.loading}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div label='' style={{ display: 'none' }}></div>
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
export default function MainModal(props) {
|
||||
const language = window.language.modals.main.navbar;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className='closeModal' onClick={props.modalClose}>×</span>
|
||||
<Tabs navbar={true}>
|
||||
<div label={language.settings} name='settings'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Settings/>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.addons} name='addons'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Addons/>
|
||||
</Suspense>
|
||||
</div>
|
||||
<div label={language.marketplace} name='marketplace'>
|
||||
<Suspense fallback={renderLoader()}>
|
||||
<Marketplace/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
75
src/components/modals/main/marketplace/Item.jsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { ArrowBack } from '@material-ui/icons';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import Lightbox from './Lightbox';
|
||||
|
||||
export default class Item extends PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
showLightbox: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const language = window.language.modals.main.marketplace.product;
|
||||
|
||||
if (!this.props.data.display_name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let warningHTML;
|
||||
if (this.props.data.quote_api) {
|
||||
warningHTML = (
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{language.quote_warning.title}</li>
|
||||
<li id='updated'>{language.quote_warning.description}</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// prevent console error
|
||||
let iconsrc = window.constants.DDG_IMAGE_PROXY + this.props.data.icon;
|
||||
if (!this.props.data.icon) {
|
||||
iconsrc = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id='item'>
|
||||
<br/>
|
||||
<ArrowBack className='backArrow' onClick={this.props.toggleFunction}/>
|
||||
<br/>
|
||||
<h1>{this.props.data.display_name}</h1>
|
||||
{this.props.button}
|
||||
<br/><br/>
|
||||
{iconsrc ? <img alt='product' draggable='false' src={iconsrc} onClick={() => this.setState({ showLightbox: true })}/> : null}
|
||||
<div className='side'>
|
||||
<div className='productInformation'>
|
||||
<ul>
|
||||
<li className='header'>{language.version}</li>
|
||||
<li>{this.props.data.version}</li>
|
||||
<br/>
|
||||
<li className='header'>{language.author}</li>
|
||||
<li>{this.props.data.author}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
{warningHTML}
|
||||
</div>
|
||||
<div className='sidebr'>
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div className='informationContainer'>
|
||||
<h1 className='overview'>{language.overview}</h1>
|
||||
<p className='description' dangerouslySetInnerHTML={{ __html: this.props.data.description }}></p>
|
||||
</div>
|
||||
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showLightbox: false })} isOpen={this.state.showLightbox} className='Modal lightboxmodal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>
|
||||
<Lightbox modalClose={() => this.setState({ showLightbox: false })} img={iconsrc}/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
15
src/components/modals/main/marketplace/Items.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function Items(props) {
|
||||
return (
|
||||
<div className='items'>
|
||||
{props.items.map((item) => (
|
||||
<div className='item' onClick={() => props.toggleFunction(item.name)} key={item.name}>
|
||||
<img alt='icon' draggable='false' src={window.constants.DDG_IMAGE_PROXY + item.icon_url} />
|
||||
<div className='details'>
|
||||
<h4>{item.display_name || item.name}</h4>
|
||||
<p>{item.author}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
src/components/modals/main/marketplace/Lightbox.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Lightbox(props) {
|
||||
window.stats.postEvent('modal', 'Opened lightbox');
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className='closeModal' onClick={props.modalClose}>×</span>
|
||||
<img src={props.img} className='lightboximg' draggable={false} alt='Item screenshot'/>
|
||||
</>
|
||||
);
|
||||
}
|
||||