Compare commits

...

59 Commits
5.1.0 ... 5.2.0

Author SHA1 Message Date
David Ralph
6cdfb9db3e chore: release 5.2 2021-08-02 13:47:06 +01:00
David Ralph
1ce6cb9419 fix: background offline interval 2021-08-01 18:20:38 +01:00
David Ralph
141da4ba36 fix: background proxy and offline mode hot reload bugs 2021-08-01 15:02:18 +01:00
David Ralph
9db7b210bc Merge branch 'main' of https://github.com/mue/mue 2021-07-29 14:18:34 +01:00
David Ralph
090ffe05f4 fix: about copyright link 2021-07-29 14:18:21 +01:00
David Ralph
66f6d0f7ed chore(translations): add messages.json files 2021-07-29 14:18:03 +01:00
dependabot[bot]
285145fdcb chore(deps): bump @material-ui/core from 5.0.0-beta.1 to 5.0.0-beta.2 (#171)
Bumps [@material-ui/core](https://github.com/mui-org/material-ui/tree/HEAD/packages/material-ui) from 5.0.0-beta.1 to 5.0.0-beta.2.
- [Release notes](https://github.com/mui-org/material-ui/releases)
- [Changelog](https://github.com/mui-org/material-ui/blob/next/CHANGELOG.md)
- [Commits](https://github.com/mui-org/material-ui/commits/v5.0.0-beta.2/packages/material-ui)

---
updated-dependencies:
- dependency-name: "@material-ui/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-27 10:15:12 +01:00
David Ralph
37df7fb08c fix: revert using api to get temperature format 2021-07-23 10:55:20 +01:00
David Ralph
5a91fa24e9 chore: update api urls 2021-07-22 21:47:08 +01:00
David Ralph
bdd277d876 feat(translations): add support for description and chrome store page 2021-07-19 20:28:40 +01:00
David Ralph
d7be6e351d fix: firefox changelog date, cleanup background a bit 2021-07-19 10:20:00 +01:00
David Ralph
c9f2af6b96 fix: font size on builds, typo on welcome modal 2021-07-18 21:53:35 +01:00
David Ralph
b426041598 fix: marketplace background settings, reset button, intervals 2021-07-18 15:54:45 +01:00
David Ralph
2d7e138b2f fix: welcome background refresh, add more privacy tab content 2021-07-18 12:20:35 +01:00
David Ralph
332e6c33ac feat: welcome modal language hot reload 2021-07-18 10:59:38 +01:00
David Ralph
99993720f2 fix: welcome modal is now responsive, fix close button with other languages, codacy fixes etc 2021-07-18 10:43:25 +01:00
David Ralph
59a888c861 feat(translations): add support for background/quote interval 2021-07-17 19:45:05 +01:00
David Ralph
6cccad0e17 feat: change background/quote interval, use api to get temperature format 2021-07-17 19:38:00 +01:00
David Ralph
8ffd0e04cb build: fix vercel 2021-07-17 15:56:49 +01:00
David Ralph
3d6884fe9a feat: timezone setting, translation file updater 2021-07-17 15:51:57 +01:00
David Ralph
2a1e26d0c4 fix: add link, date picker, photo information, uninstall button 2021-07-17 13:43:18 +01:00
David Ralph
b842bd935e fix: discord link in about now works again 2021-07-15 17:41:51 +01:00
David Ralph
3a47089a00 fix: welcome start, marketplace item page is now responsive, update text etc 2021-07-15 17:39:38 +01:00
dependabot[bot]
e1f43dc343 chore(deps): bump @material-ui/icons from 5.0.0-beta.0 to 5.0.0-beta.1 (#168)
Bumps [@material-ui/icons](https://github.com/mui-org/material-ui/tree/HEAD/packages/material-ui-icons) from 5.0.0-beta.0 to 5.0.0-beta.1.
- [Release notes](https://github.com/mui-org/material-ui/releases)
- [Changelog](https://github.com/mui-org/material-ui/blob/next/CHANGELOG.md)
- [Commits](https://github.com/mui-org/material-ui/commits/v5.0.0-beta.1/packages/material-ui-icons)

---
updated-dependencies:
- dependency-name: "@material-ui/icons"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: David Ralph <ohlookitsderpy@protonmail.com>
2021-07-15 15:09:31 +01:00
dependabot[bot]
75b3c23a38 chore(deps-dev): bump css-loader from 5.2.7 to 6.0.0 (#169)
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.7 to 6.0.0.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.7...v6.0.0)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-15 15:09:03 +01:00
dependabot[bot]
7a271190d5 chore(deps): bump @material-ui/core from 5.0.0-beta.0 to 5.0.0-beta.1 (#167)
Bumps [@material-ui/core](https://github.com/mui-org/material-ui/tree/HEAD/packages/material-ui) from 5.0.0-beta.0 to 5.0.0-beta.1.
- [Release notes](https://github.com/mui-org/material-ui/releases)
- [Changelog](https://github.com/mui-org/material-ui/blob/next/CHANGELOG.md)
- [Commits](https://github.com/mui-org/material-ui/commits/v5.0.0-beta.1/packages/material-ui)

---
updated-dependencies:
- dependency-name: "@material-ui/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-15 15:09:00 +01:00
David Ralph
c725ad4cc3 style: improve item page, update photo width/height code 2021-07-15 15:08:37 +01:00
David Ralph
c07d7ecbb0 feat: background resolution is now automatic and more accurate 2021-07-12 21:48:08 +01:00
David Ralph
99ffc82de0 refactor: update widgets and fix font change 2021-07-12 11:34:05 +01:00
David Ralph
7840b48727 fix: missing unicode-range 2021-07-11 15:11:12 +01:00
David Ralph
401baa6653 fix: noscript message css, remove woff from build 2021-07-11 15:08:35 +01:00
David Ralph
70fc204e70 perf: optimise search and autocomplete 2021-07-11 14:29:32 +01:00
David Ralph
a01d778f65 perf: new date picker and add purecomponent to more things 2021-07-10 21:59:53 +01:00
David Ralph
ff3e2caf49 build: fix vercel dev builds and mention vercel in readme 2021-07-06 19:42:58 +01:00
David Ralph
95614a383f fix: welcome svg, privacy welcome tab, marketplace optimisations, various widget fixes etc
Co-authored-by: Alex Sparkes <turbomarshmello@gmail.com>
2021-07-06 19:38:20 +01:00
David Ralph
44fc24951f fix: import settings on welcome, progress bar, custom js etc 2021-07-04 12:28:28 +01:00
David Ralph
19bc802f09 feat: navbar hot reload, quick links zoom, add example images to welcome modal, fixes 2021-07-03 14:48:49 +01:00
David Ralph
ba9f2e01c8 perf: optimise svg 2021-07-02 22:04:11 +01:00
David Ralph
8ece0a7eb0 feat(translations): add translation support to welcome modal, fix clock 2021-07-02 21:59:58 +01:00
David Ralph
d91490874c fix: add that fix for minutes, not just seconds 2021-07-02 21:42:10 +01:00
David Ralph
6a3a367cda fix: close #164 2021-07-02 21:39:25 +01:00
dependabot[bot]
84e6532a80 chore(deps-dev): bump mini-css-extract-plugin from 1.6.2 to 2.0.0 (#163)
Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 1.6.2 to 2.0.0.
- [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v1.6.2...v2.0.0)

---
updated-dependencies:
- dependency-name: mini-css-extract-plugin
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-02 21:32:00 +01:00
David Ralph
cf36ced2a7 feat: new welcome modal, refactor many things, various bug fixes, use material-ui v5 etc
Co-authored-by: Alex Sparkes <turbomarshmello@gmail.com>
2021-07-02 21:29:33 +01:00
David Ralph
c6b65f943a fix: various settings bug fixes and style changes 2021-06-30 15:25:34 +01:00
David Ralph
6aa1c6b0ea fix: weather with offline mode, maximise fixes, voice search hot reload 2021-06-30 14:11:45 +01:00
David Ralph
2dcaa5270d feat: locally store stats so users can see them (no ui yet) 2021-06-30 11:27:54 +01:00
David Ralph
1d44b2792e chore: update readme 2021-06-23 12:19:12 +01:00
David Ralph
d0577ded59 chore: revert other changes 2021-06-23 12:00:12 +01:00
David Ralph
ae5a07c756 build: revert changes 2021-06-23 11:57:36 +01:00
David Ralph
5d3418a8af build: optimise production builds, automate creating zip files 2021-06-22 21:07:56 +01:00
David Ralph
50353c9e49 build: cleanup webpack config 2021-06-22 19:45:13 +01:00
David Ralph
ea850fae56 perf: replace react-device-detect with custom code and allow edge to use voice search 2021-06-22 12:41:25 +01:00
dependabot[bot]
ba11ac171e chore(deps-dev): bump sass-loader from 7.3.1 to 12.1.0 (#162)
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 7.3.1 to 12.1.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v7.3.1...v12.1.0)

---
updated-dependencies:
- dependency-name: sass-loader
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-22 11:47:24 +01:00
David Ralph
faace9012a feat: add experimental send event feature 2021-06-22 11:46:08 +01:00
David Ralph
90ebfeedc5 chore: release 5.1.1 2021-06-22 10:19:37 +01:00
David Ralph
cfc56c6abf fix: urgent fix for background blur 2021-06-22 10:17:51 +01:00
David Ralph
d7784e7414 chore: update events 2021-06-21 23:03:47 +01:00
David Ralph
7990286e9a style: cleanup constants 2021-06-21 17:45:06 +01:00
David Ralph
f7c39eeebb feat: add opt-in umami analytics (WIP) 2021-06-21 17:42:14 +01:00
119 changed files with 2530 additions and 666 deletions

View File

@@ -5,4 +5,4 @@ indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
insert_final_newline = true

View File

@@ -1,4 +1,4 @@
module.exports = {
extends: 'react-app',
parser: '@babel/eslint-parser'
extends: 'react-app',
parser: '@babel/eslint-parser'
};

View File

@@ -0,0 +1,20 @@
---
name: Feature Request
about: Request things to be added to mue
title: "[Feature Request]"
labels: enhancement
assignees: ''
---
**Description**
A clear and concise description of what you want adding to Mue. If it's related to a problem, mention so.
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Design**
Images or Figma prototypes of what your idea would be like.
**Additional context**
Add any other context about the feature request here.

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ yarn-error.log
.eslintcache
stats.json
yarn.lock
*.zip

View File

@@ -7,7 +7,7 @@
<br>
[![Microsoft Edge](https://img.shields.io/badge/dynamic/json?style=flat-square&label=microsoft%20edge&query=%24.version&url=https%3A%2F%2Fmicrosoftedge.microsoft.com%2Faddons%2Fgetproductdetailsbycrxid%2Faepnglgjfokepefimhbnibfjekidhmja)](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja) [![Firefox](https://img.shields.io/amo/v/mue?label=firefox&style=flat-square)](https://addons.mozilla.org/firefox/addon/mue) [![Chrome](https://img.shields.io/chrome-web-store/v/bngmbednanpcfochchhgbkookpiaiaid?label=chrome&style=flat-square)](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers
Mue is a fast, open and free-to-use browser extension that gives a new, fresh and customisable tab page to modern browsers.
<br>
@@ -19,7 +19,7 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
* [Chrome](#chrome)
* [Firefox](#firefox)
* [Edge Chromium](#edge-chromium)
* [Naver](#naver)
* [Whale](#whale)
* [Other](#other)
* [Contributing](#development)
* [Translations](#translations)
@@ -36,17 +36,17 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
* Fast and free
* Supports multiple browsers
* Actively developed and open source
* Automatically updating API (no tracking) with new photos, quotes and offline mode
* 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
* Navbar with copy button, favourite background, notes feature etc
* Marketplace - download custom photo packs made by the community
* [Marketplace](https://github.com/mue/marketplace) - download custom photo packs, quote packs and preset settings made by the community
### Planned Features
Please see our [roadmap](https://github.com/mue/mue/projects)
## Installation
*A demo of the tab can be found [here](https://demo.muetab.com)*
*A demo of the tab can be found [here](https://demo.muetab.com), and the latest GitHub commit build [here](https://mue.vercel.app)*
### Chrome
[![Chrome Web Store Logo](assets/chrome.png)](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
<br>
@@ -60,16 +60,14 @@ Please see our [roadmap](https://github.com/mue/mue/projects)
### Edge (Chromium)
[Microsoft Edge Addons](https://microsoftedge.microsoft.com/addons/detail/aepnglgjfokepefimhbnibfjekidhmja)
### Naver
### Whale
[Whale Store](https://store.whale.naver.com/detail/ecllekeilcmicbfkkiknfdddbogibbnc)
### Other
Please note that we have dropped support for Opera as of Mue 5.0
[GitHub Releases](https://github.com/mue/mue/releases)
## Development
This section has moved to the [documentation](https://docs.muetab.com/development#mue-tab).
Please see the [documentation](https://docs.muetab.com/development#mue-tab).
### Translations
Please see the [documentation](https://docs.muetab.com/translations).
@@ -81,16 +79,17 @@ Please see the [documentation](https://docs.muetab.com/translations).
[Isaac Saunders](https://github.com/eartharoid) - QA, development, photographer <br/>
[Wessel Tip](https://github.com/Wessel) - Development <br/>
### Translators
[Wessel Tip](https://github.com/Wessel), [Heimen Stoffels](https://github.com/Vistaus) - Dutch<br/>
[Alex Sparkes](https://github.com/alexsparkes), [Maxime](https://github.com/exiam) - French<br/>
[Anders](https://github.com/FuryingFox) - Norwegian<br/>
[Pronin Egor](https://github.com/MrZillaGold) - Russian<br/>
[Vicente](https://github.com/Vicente015) - Spanish<br/>
[Austin Huang](https://github.com/austinhuang0131) - Chinese (Simplified)<br/>
[FreeFun](https://github.com/xXFreeFunXx) - German<br/>
[Wessel Tip](https://github.com/Wessel), [Heimen Stoffels](https://github.com/Vistaus) - Dutch <br/>
[Alex Sparkes](https://github.com/alexsparkes), [Maxime](https://github.com/exiam) - French <br/>
[Anders](https://github.com/FuryingFox) - Norwegian <br/>
[Pronin Egor](https://github.com/MrZillaGold) - Russian <br/>
[Vicente](https://github.com/Vicente015) - Spanish <br/>
[Austin Huang](https://github.com/austinhuang0131) - Chinese (Simplified) <br/>
[FreeFun](https://github.com/xXFreeFunXx) - German <br/>
### Contributors
Many thanks to the photographers [here](https://api.muetab.com/images/photographers) for letting us use their wonderful photographs.
And finally, a big thank you to all the other [contributors](https://github.com/mue/mue/graphs/contributors)!
### Resources
[Pexels](https://pexels.com), [Unsplash](https://unsplash.com) - Stock photos used for offline mode
[Pexels](https://pexels.com), [Unsplash](https://unsplash.com) - Stock photos used for offline mode <br/>
[Undraw](https://undraw.co) - Welcome modal images

View File

@@ -1,6 +1,6 @@
module.exports = {
presets: ['@babel/preset-env', ['@babel/preset-react', {
'runtime': 'automatic'
runtime: 'automatic'
}]],
plugins: ['@babel/plugin-proposal-class-properties', '@babel/transform-runtime', 'babel-plugin-transform-react-class-to-function', '@babel/plugin-transform-react-constant-elements']
};

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -0,0 +1,8 @@
{
"name": {
"message": "Mue"
},
"description": {
"message": "Fast, open and free-to-use new tab page for modern browsers."
}
}

View File

@@ -1,9 +1,10 @@
{
"manifest_version": 2,
"offline_enabled": true,
"name": "Mue",
"description": "Fast, open and free-to-use new tab page for modern browsers.",
"version": "5.1.0",
"default_locale": "en",
"name": "__MSG_name__",
"description": "__MSG_description__",
"version": "5.2.0",
"homepage_url": "https://muetab.com",
"browser_action": {
"default_icon": "icons/128x128.png"

View File

@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Mue",
"description": "Fast, open and free-to-use new tab page for modern browsers.",
"version": "5.1.0",
"version": "5.2.0",
"homepage_url": "https://muetab.com",
"browser_action": {
"default_icon": "icons/128x128.png"

View File

@@ -9,18 +9,18 @@
"homepage": "https://muetab.com",
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
"license": "BSD-3-Clause",
"version": "5.1.0",
"version": "5.2.0",
"dependencies": {
"@fontsource/lexend-deca": "^4.4.5",
"@fontsource/montserrat": "^4.4.5",
"@material-ui/core": "4.11.4",
"@material-ui/icons": "4.11.2",
"fetch-jsonp": "^1.1.3",
"@emotion/react": "^11.4.0",
"@emotion/styled": "^11.3.0",
"@fontsource/lexend-deca": "4.4.5",
"@fontsource/montserrat": "4.4.5",
"@material-ui/core": "5.0.0-beta.2",
"@material-ui/icons": "5.0.0-beta.1",
"date-fns-tz": "^1.1.6",
"react": "17.0.2",
"react-clock": "3.0.0",
"react-color-gradient-picker": "0.1.2",
"react-day-picker": "7.4.10",
"react-device-detect": "1.17.0",
"react-dom": "17.0.2",
"react-modal": "3.14.3",
"react-sortable-hoc": "2.0.0",
@@ -28,33 +28,35 @@
"weather-icons-react": "1.2.0"
},
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/eslint-parser": "^7.14.5",
"@babel/core": "^7.14.8",
"@babel/eslint-parser": "^7.14.9",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-transform-react-constant-elements": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/preset-env": "^7.14.5",
"@babel/preset-env": "^7.14.9",
"@babel/preset-react": "^7.14.5",
"@eartharoid/deep-merge": "^0.0.1",
"babel-loader": "^8.2.2",
"babel-plugin-transform-react-class-to-function": "^1.2.2",
"copy-webpack-plugin": "^9.0.0",
"css-loader": "^5.2.6",
"eslint": "^7.28.0",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.2.0",
"date-fns": "^2.23.0",
"eslint": "^7.32.0",
"eslint-config-react-app": "^6.0.0",
"html-webpack-plugin": "^5.3.1",
"mini-css-extract-plugin": "^1.6.0",
"sass": "^1.35.1",
"sass-loader": "^7.3.1",
"html-webpack-plugin": "^5.3.2",
"mini-css-extract-plugin": "^2.1.0",
"sass": "^1.37.0",
"sass-loader": "^12.1.0",
"source-map-loader": "^3.0.0",
"webpack": "^5.39.1",
"webpack": "^5.47.1",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
"webpack-dev-server": "^4.0.0-rc.0"
},
"scripts": {
"start": "webpack serve",
"build": "webpack --mode=production",
"chrome": "cp manifest/chrome.json build/manifest.json",
"firefox": "cp manifest/firefox.json build/manifest.json"
"chrome": "cp manifest/chrome.json build/manifest.json && cp -r manifest/_locales build/_locales",
"firefox": "rm -rf build/_locales && cp manifest/firefox.json build/manifest.json"
},
"browserslist": {
"production": [

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

View 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

View 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

View File

@@ -11,21 +11,17 @@
<body>
<noscript>
<style>
@font-face {
font-family: 'Lexend Deca';
src: url('./static/media/lexend-deca-latin-400-normal.35a9aeba.woff2');
}
*, a {
font-family: 'Lexend Deca', sans-serif;
text-align: center;
color: black;
background: white !important;
}
@media (prefers-color-scheme: dark) {
*, a {
color: white;
background: #2f3640;
background: #2f3640 !important;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View 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');
});

View File

@@ -11,14 +11,10 @@ import { ToastContainer } from 'react-toastify';
export default class App extends React.PureComponent {
componentDidMount() {
if (!localStorage.getItem('firstRun')) {
SettingsFunctions.setDefaultSettings();
window.location.reload();
}
// 4.0 -> 5.0 (the key below is only on 5.0)
// now featuring 5.0 -> 5.1
if (!localStorage.getItem('order') || !localStorage.getItem('autocompleteProvider')) {
// the firstRun check was moved here because the old function was useless
if (!localStorage.getItem('firstRun') || !localStorage.getItem('stats')) {
SettingsFunctions.moveSettings();
window.location.reload();
}
@@ -30,6 +26,8 @@ export default class App extends React.PureComponent {
SettingsFunctions.loadSettings(true);
}
});
window.stats.tabLoad();
}
render() {

View File

@@ -7,14 +7,12 @@ export default class Autocomplete extends React.PureComponent {
super(props);
this.state = {
filtered: [],
showList: false,
input: ''
};
this.enabled = (localStorage.getItem('autocomplete') === 'true');
}
onChange = (e) => {
if (this.enabled === false) {
if (localStorage.getItem('autocomplete') !== 'true') {
return this.setState({
input: e.target.value
});
@@ -22,7 +20,6 @@ export default class Autocomplete extends React.PureComponent {
this.setState({
filtered: this.props.suggestions.filter((suggestion) => suggestion.toLowerCase().indexOf(e.target.value.toLowerCase()) > -1),
showList: true,
input: e.target.value
});
@@ -32,7 +29,6 @@ export default class Autocomplete extends React.PureComponent {
onClick = (e) => {
this.setState({
filtered: [],
showList: false,
input: e.target.innerText
});
@@ -42,20 +38,19 @@ export default class Autocomplete extends React.PureComponent {
render() {
let autocomplete = null;
if (this.state.showList && this.state.input) {
if (this.state.filtered.length && localStorage.getItem('autocomplete') === 'true') {
autocomplete = (
<ul className='suggestions'>
{this.state.filtered.map((suggestion) => {
return (
<li key={suggestion} onClick={this.onClick}>
{suggestion}
</li>
);
})}
</ul>
);
}
// length will only be > 0 if enabled
if (this.state.filtered.length > 0 && this.state.input.length > 0) {
autocomplete = (
<ul className='suggestions'>
{this.state.filtered.map((suggestion) => {
return (
<li key={suggestion} onClick={this.onClick}>
{suggestion}
</li>
);
})}
</ul>
);
}
return (
@@ -64,7 +59,6 @@ export default class Autocomplete extends React.PureComponent {
type='text'
onChange={this.onChange}
value={this.state.input}
name={this.props.name || 'name'}
placeholder={this.props.placeholder || ''}
autoComplete='off'
id={this.props.id || ''} />

View File

@@ -13,6 +13,7 @@ export default class ErrorBoundary extends React.PureComponent {
static getDerivedStateFromError(error) {
console.log(error);
window.stats.postEvent('modal', 'Error occurred');
return {
error: true
};

View File

@@ -1,5 +1,7 @@
import React from 'react';
import EventBus from '../../modules/helpers/eventbus';
import Main from './main/Main';
import Navbar from '../widgets/navbar/Navbar';
@@ -27,6 +29,7 @@ export default class Modals extends React.PureComponent {
this.setState({
welcomeModal: true
});
window.stats.postEvent('modal', 'Opened welcome');
}
// hide refresh reminder once the user has refreshed the page
@@ -38,24 +41,36 @@ export default class Modals extends React.PureComponent {
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 (
<>
<Navbar openModal={(modal) => this.setState({ [modal]: true })}/>
<Modal closeTimeoutMS={300} id='modal' onRequestClose={() => this.setState({ mainModal: false })} isOpen={this.state.mainModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
<Main modalClose={() => this.setState({ mainModal: false })}/>
<Navbar openModal={(modal) => this.toggleModal(modal, true)}/>
<Modal closeTimeoutMS={300} id='modal' onRequestClose={() => this.toggleModal('mainModal', false)} isOpen={this.state.mainModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
<Main modalClose={() => this.toggleModal('mainModal', false)}/>
</Modal>
<React.Suspense fallback={renderLoader()}>
<Modal closeTimeoutMS={300} onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className='Modal welcomemodal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
<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.setState({ feedbackModal: false })} isOpen={this.state.feedbackModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
<Feedback modalClose={() => this.setState({ feedbackModal: false })}/>
<Modal closeTimeoutMS={300} onRequestClose={() => this.toggleModal('feedbackModal', false)} isOpen={this.state.feedbackModal} className='Modal mainModal' overlayClassName='Overlay' ariaHideApp={false}>
<Feedback modalClose={() => this.toggleModal('feedbackModal', false)}/>
</Modal>
</React.Suspense>
</>
);
}
}
}

View File

@@ -18,7 +18,7 @@ const renderLoader = () => (
</div>
</div>
</div>
<div label='' style={{ 'display': 'none' }}></div>
<div label='' style={{ display: 'none' }}></div>
</Tabs>
);
@@ -29,17 +29,17 @@ export default function MainModal(props) {
<>
<span className='closeModal' onClick={props.modalClose}>&times;</span>
<Tabs navbar={true}>
<div label={language.settings}>
<div label={language.settings} name='settings'>
<React.Suspense fallback={renderLoader()}>
<Settings/>
</React.Suspense>
</div>
<div label={language.addons}>
<div label={language.addons} name='addons'>
<React.Suspense fallback={renderLoader()}>
<Addons/>
</React.Suspense>
</div>
<div label={language.marketplace}>
<div label={language.marketplace} name='marketplace'>
<React.Suspense fallback={renderLoader()}>
<Marketplace/>
</React.Suspense>

View File

@@ -34,7 +34,7 @@ export default class Item extends React.PureComponent {
}
// prevent console error
let iconsrc = window.constants.DDG_PROXY + this.props.data.icon;
let iconsrc = window.constants.DDG_IMAGE_PROXY + this.props.data.icon;
if (!this.props.data.icon) {
iconsrc = null;
}
@@ -45,27 +45,28 @@ export default class Item extends React.PureComponent {
<ArrowBackIcon className='backArrow' onClick={this.props.toggleFunction}/>
<br/>
<h1>{this.props.data.display_name}</h1>
<br/>
{this.props.button}
<br/>
<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 className='productInformation'>
<ul>
{/* <li className='header'>{language.last_updated}</li>
<li>{this.props.data.updated}</li>
<br/>*/}
<li className='header'>{language.version}</li>
<li>{this.props.data.version}</li>
<br/>
<li className='header'>{language.author}</li>
<li>{this.props.data.author}</li>
</ul>
</div>
<br/>
{warningHTML}
</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}/>

View File

@@ -3,7 +3,7 @@ export default function Items(props) {
<div className='items'>
{props.items.map((item) => (
<div className='item' onClick={() => props.toggleFunction(item.name)} key={item.name}>
<img alt='icon' draggable='false' src={window.constants.DDG_PROXY + item.icon_url} />
<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>

View File

@@ -1,4 +1,6 @@
export default function Lightbox(props) {
window.stats.postEvent('modal', 'Opened lightbox');
return (
<>
<span className='closeModal' onClick={props.modalClose}>&times;</span>

View File

@@ -42,6 +42,7 @@ export default class Added extends React.PureComponent {
},
button: this.buttons.uninstall
});
window.stats.postEvent('marketplace', 'Item viewed');
} else {
this.setState({
item: {}
@@ -58,9 +59,11 @@ export default class Added extends React.PureComponent {
button: '',
installed: JSON.parse(localStorage.getItem('installed'))
});
window.stats.postEvent('marketplace', 'Uninstall');
}
sortAddons(value) {
sortAddons(value, sendEvent) {
let installed = JSON.parse(localStorage.getItem('installed'));
switch (value) {
case 'newest':
@@ -82,10 +85,14 @@ export default class Added extends React.PureComponent {
this.setState({
installed: installed
});
if (sendEvent) {
window.stats.postEvent('marketplace', 'Sort');
}
}
componentDidMount() {
this.sortAddons(localStorage.getItem('sortAddons'));
this.sortAddons(localStorage.getItem('sortAddons'), false);
}
render() {

View File

@@ -67,6 +67,8 @@ export default class Marketplace extends React.PureComponent {
},
button: button
});
window.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
} else {
this.setState({
item: {}
@@ -75,7 +77,7 @@ export default class Marketplace extends React.PureComponent {
}
async getItems() {
const { data } = await (await fetch(window.constants.MARKETPLACE_URL + '/all', { signal: this.controller.signal })).json();
const { data } = await (await fetch(window.constants.MARKETPLACE_URL + '/items/' + this.props.type, { signal: this.controller.signal })).json();
const featured = await (await fetch(window.constants.MARKETPLACE_URL + '/featured', { signal: this.controller.signal })).json();
if (this.controller.signal.aborted === true) {
@@ -83,13 +85,13 @@ export default class Marketplace extends React.PureComponent {
}
this.setState({
items: data[this.props.type],
oldItems: data[this.props.type],
items: data,
oldItems: data,
featured: featured.data,
done: true
});
this.sortMarketplace(localStorage.getItem('sortMarketplace'));
this.sortMarketplace(localStorage.getItem('sortMarketplace'), false);
}
manage(type) {
@@ -103,9 +105,12 @@ export default class Marketplace extends React.PureComponent {
this.setState({
button: (type === 'install') ? this.buttons.uninstall : this.buttons.install
});
window.stats.postEvent('marketplace-item', `${this.state.item.display_name} ${(type === 'install' ? 'installed': 'uninstalled')}`);
window.stats.postEvent('marketplace', (type === 'install' ? 'Install': 'Uninstall'));
}
sortMarketplace(value) {
sortMarketplace(value, sendEvent) {
let items = this.state.oldItems;
switch (value) {
case 'a-z':
@@ -127,6 +132,10 @@ export default class Marketplace extends React.PureComponent {
items: items,
sortType: value
});
if (sendEvent) {
window.stats.postEvent('marketplace', 'Sort');
}
}
componentDidMount() {
@@ -154,11 +163,15 @@ export default class Marketplace extends React.PureComponent {
};
const featured = () => {
const openFeatured = () => {
window.stats.postEvent('marketplace', 'Featured clicked');
window.open(this.state.featured.buttonLink);
}
return (
<div className='featured' style={{ 'backgroundColor': this.state.featured.colour }}>
<div className='featured' style={{ backgroundColor: this.state.featured.colour }}>
<p>{this.state.featured.title}</p>
<h1>{this.state.featured.name}</h1>
<button className='addToMue' onClick={() => window.open(this.state.featured.buttonLink)}>{this.state.featured.buttonText}</button>
<button className='addToMue' onClick={() => openFeatured()}>{this.state.featured.buttonText}</button>
</div>
);
}

View File

@@ -10,6 +10,7 @@ export default function Sideload() {
const install = (input) => {
MarketplaceFunctions.install(input.type, input);
toast(window.language.toasts.installed);
window.stats.postEvent('marketplace', 'Sideload');
};
return (

View File

@@ -348,6 +348,7 @@ li {
min-height: 300px !important;
max-width: 300px !important;
margin: auto;
font-size: 1rem;
h4 {
cursor: initial;
@@ -358,6 +359,9 @@ li {
.resetfooter {
position: absolute;
bottom: 20px;
width: 300px;
justify-content: center;
display: flex;
button.reset {
margin-right: 43px;
@@ -410,3 +414,7 @@ h3 {
h5 {
font-size: 0.8rem;
}
.checkbox svg {
fill: var(--modal-text) !important;
}

View File

@@ -29,7 +29,8 @@
border: 2px solid #ff4757;
color: #ff4757;
margin-top: 5px;
float: right;
margin-top: -10px;
&:hover {
background: #ff4757;
@@ -39,10 +40,23 @@
.addToMue {
@extend %storebutton;
margin-top: 12px;
float: right;
margin-top: -10px;
}
.sideload {
display: inline;
margin-top: 0px;
float: none !important;
}
button.round {
margin-left: 5px;
width: 30px;
height: 30px;
border-radius: 50%;
text-align: center;
line-height: 3px;
vertical-align: middle;
padding: 10px;
}

View File

@@ -89,6 +89,16 @@
}
}
@media only screen and (max-width: 1680px) {
.side {
float: none !important;
}
.sidebr {
display: none;
}
}
p.author {
margin-top: -5px;
}
@@ -98,6 +108,15 @@ p.author {
font-size: 40px;
line-height: 20px;
}
img {
float: left;
}
}
.side {
float: right;
margin-left: 20px;
}
#item>h1,
@@ -107,7 +126,7 @@ p.author {
p.description {
margin-top: 0px;
max-width: 400px;
max-width: 800px;
}
.emptyMessage {
@@ -119,6 +138,10 @@ p.description {
position: absolute;
width: 300px;
h1 {
font-size: 2rem;
}
svg {
font-size: 50px;
margin-bottom: -20px;
@@ -136,7 +159,8 @@ p.description {
}
.informationContainer {
margin-top: 20px;
margin-top: 150px;
position: absolute;
}
.productInformation {
@@ -192,6 +216,7 @@ p.description {
h1 {
margin-top: -20px;
font-size: 2rem;
}
}

View File

@@ -15,7 +15,6 @@
.reset {
@extend %settingsButton;
margin-left: 5px;
background-color: map-get($button-colours, 'reset');
border: 2px solid map-get($button-colours, 'reset');

View File

@@ -24,7 +24,6 @@ select {
}
}
// safari dropdown
@supports (-webkit-hyphens: none) {
select {

View File

@@ -47,6 +47,21 @@ input {
border-radius: 100%;
}
}
&[type=date] {
width: 200px;
color: var(--modal-text);
background: var(--sidebar);
border: none;
padding: 10px 10px;
border-radius: 5px;
}
}
.dark {
::-webkit-calendar-picker-indicator {
filter: invert(1);
}
}
h4 {
@@ -139,7 +154,7 @@ legend {
border-radius: 0.7em;
h1 {
font-size: 1em;
font-size: 1rem;
}
}
@@ -183,6 +198,7 @@ legend {
.changelogtab {
h1 {
max-width: 85%;
font-size: 2rem;
}
img {
@@ -219,4 +235,4 @@ input[type=number] {
vertical-align: sub;
font-size: 1.4rem;
}
}
}

View File

@@ -1,7 +1,6 @@
import React from 'react';
import EventBus from '../../../../modules/helpers/eventbus';
import SettingsFunctions from '../../../../modules/helpers/settings';
import CheckboxUI from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
@@ -15,12 +14,15 @@ export default class Checkbox extends React.PureComponent {
}
handleChange = () => {
SettingsFunctions.setItem(this.props.name);
const value = (this.state.checked === true) ? false : true;
localStorage.setItem(this.props.name, value);
this.setState({
checked: (this.state.checked === true) ? false : true
checked: value
});
window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'block';
@@ -43,7 +45,7 @@ export default class Checkbox extends React.PureComponent {
return (
<>
<FormControlLabel
control={<CheckboxUI name={this.props.name} color='primary' checked={this.state.checked} onChange={this.handleChange} />}
control={<CheckboxUI name={this.props.name} color='primary' className='checkbox' checked={this.state.checked} onChange={this.handleChange} />}
label={text}
/>
<br/>

View File

@@ -22,6 +22,8 @@ export default class Dropdown extends React.PureComponent {
return;
}
window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
this.setState({
value: value,
title: e.target[e.target.selectedIndex].text
@@ -55,7 +57,7 @@ export default class Dropdown extends React.PureComponent {
return (
<>
{this.getLabel()}
<select id={this.props.name} value={this.state.value} onChange={this.onChange} style={{width: `${(8*this.state.title.length) + 50}px`}}>
<select id={this.props.name} value={this.state.value} onChange={this.onChange} style={{ width: `${(8*this.state.title.length) + 50}px` }}>
{this.props.children}
</select>
</>

View File

@@ -29,6 +29,8 @@ export default class Radio extends React.PureComponent {
value: value
});
window.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'block';

View File

@@ -1,21 +1,30 @@
import CloseIcon from '@material-ui/icons/Close';
import DeleteIcon from '@material-ui/icons/Delete';
import SettingsFunctions from '../../../../modules/helpers/settings';
export default function ResetModal(props) {
const language = window.language.modals.main.settings.sections.advanced.reset_modal;
const reset = () => {
window.stats.postEvent('setting', 'Reset');
SettingsFunctions.setDefaultSettings('reset');
window.location.reload();
}
};
return (
<>
<h3 style={{ 'textAlign': 'center' }}>{language.title}</h3>
<h4>{language.question}</h4>
<p>{language.information}</p>
<h1 style={{ textAlign: 'center' }}>{language.title}</h1>
<span>{language.question}</span>
<br/><br/>
<span>{language.information}</span>
<div className='resetfooter'>
<button className='reset' style={{ 'marginLeft': '0' }} onClick={() => reset()}>{window.language.modals.main.settings.buttons.reset}</button>
<button className='import' style={{ 'marginLeft': '5px' }} onClick={props.modalClose}>{language.cancel}</button>
<button className='round reset' style={{ marginLeft: 0 }} onClick={() => reset()}>
<DeleteIcon/>
</button>
<button className='round import' style={{ marginLeft: '5px' }} onClick={props.modalClose}>
<CloseIcon/>
</button>
</div>
</>
);

View File

@@ -1,7 +1,6 @@
import React from 'react';
import EventBus from '../../../../modules/helpers/eventbus';
import SettingsFunctions from '../../../../modules/helpers/settings';
import SwitchUI from '@material-ui/core/Switch';
import FormControlLabel from '@material-ui/core/FormControlLabel';
@@ -15,12 +14,15 @@ export default class Switch extends React.PureComponent {
}
handleChange = () => {
SettingsFunctions.setItem(this.props.name);
const value = (this.state.checked === true) ? false : true;
localStorage.setItem(this.props.name, value);
this.setState({
checked: (this.state.checked === true) ? false : true
checked: value
});
window.stats.postEvent('setting', `${this.props.name} ${(this.state.checked === true) ? 'enabled' : 'disabled'}`);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'block';

View File

@@ -3,7 +3,7 @@ import React from 'react';
import Tooltip from '../../../../helpers/tooltip/Tooltip';
import EmailIcon from '@material-ui/icons/Email';
import TwitterIcon from '@material-ui/icons/Twitter';
import ForumIcon from '@material-ui/icons/Forum';
import ChatIcon from '@material-ui/icons/Chat';
import InstagramIcon from '@material-ui/icons/Instagram';
import FacebookIcon from '@material-ui/icons/Facebook';
@@ -28,9 +28,9 @@ export default class About extends React.PureComponent {
let contributors, sponsors, photographers, versionData;
try {
versionData = await (await fetch(window.constants.GITHUB_URL + '/repos/mue/mue/releases', { signal: this.controller.signal })).json();
versionData = await (await fetch(window.constants.GITHUB_URL + '/repos/' + window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/releases', { signal: this.controller.signal })).json();
contributors = await (await fetch(window.constants.GITHUB_URL + '/repos/mue/mue/contributors', { signal: this.controller.signal })).json();
contributors = await (await fetch(window.constants.GITHUB_URL + '/repos/'+ window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/contributors', { signal: this.controller.signal })).json();
sponsors = (await (await fetch(window.constants.SPONSORS_URL + '/list', { signal: this.controller.signal })).json()).sponsors;
photographers = await (await fetch(window.constants.API_URL + '/images/photographers', { signal: this.controller.signal })).json();
@@ -57,6 +57,7 @@ export default class About extends React.PureComponent {
}
this.setState({
// exclude bots
contributors: contributors.filter((contributor) => !contributor.login.includes('bot')),
sponsors: sponsors,
update: updateMsg,
@@ -87,22 +88,23 @@ export default class About extends React.PureComponent {
return (
<>
<h2>{this.language.title}</h2>
<img draggable='false' className='aboutLogo' src='./././icons/logo_horizontal.png' alt='Mue logo'></img>
<p>{this.language.copyright} 2018-{new Date().getFullYear()} <a href='https://github.com/mue/mue/graphs/contributors' className='aboutLink' target='_blank' rel='noopener noreferrer'>The Mue Authors</a> (BSD-3 License)</p>
<img draggable='false' className='aboutLogo' src='./././icons/logo_horizontal.png' alt='Logo'></img>
<p>{this.language.copyright} {window.constants.COPYRIGHT_YEAR}-{new Date().getFullYear()} <a href={'https://github.com/' + window.constants.ORG_NAME + '/' + window.constants.REPO_NAME + '/graphs/contributors'} className='aboutLink' target='_blank' rel='noopener noreferrer'>{window.constants.COPYRIGHT_NAME}</a> ({window.constants.COPYRIGHT_LICENSE})</p>
<p>{this.language.version.title} {window.constants.VERSION} ({this.state.update})</p>
<a href={window.constants.PRIVACY_URL} className='aboutLink' target='_blank' rel='noopener noreferrer' style={{ fontSize: '1rem' }}>{window.language.modals.welcome.sections.privacy.links.privacy_policy}</a>
<h3>{this.language.contact_us}</h3>
<a href='mailto:hello@muetab.com' className='aboutIcon' target='_blank' rel='noopener noreferrer'><EmailIcon/></a>
<a href='https://twitter.com/getmue' className='aboutIcon' target='_blank' rel='noopener noreferrer'><TwitterIcon/></a>
<a href='https://instagram.com/mue.tab' className='aboutIcon' target='_blank' rel='noopener noreferrer'><InstagramIcon/></a>
<a href='https://facebook.com/muetab' className='aboutIcon' target='_blank' rel='noopener noreferrer'><FacebookIcon/></a>
<a href='https://discord.gg/zv8C9F8' className='aboutIcon' target='_blank' rel='noopener noreferrer'><ForumIcon/></a>
<a href={'mailto:' + window.constants.EMAIL} className='aboutIcon' target='_blank' rel='noopener noreferrer'><EmailIcon/></a>
<a href={'https://twitter.com/' + window.constants.TWITTER_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><TwitterIcon/></a>
<a href={'https://instagram.com/' + window.constants.INSTAGRAM_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><InstagramIcon/></a>
<a href={'https://facebook.com/' + window.constants.FACEBOOK_HANDLE} className='aboutIcon' target='_blank' rel='noopener noreferrer'><FacebookIcon/></a>
<a href={'https://discord.gg/' + window.constants.DISCORD_SERVER} className='aboutIcon' target='_blank' rel='noopener noreferrer'><ChatIcon/></a>
<h3>{this.language.support_mue}</h3>
<p>
<a href='https://github.com/sponsors/davidjcralph' className='aboutLink' target='_blank' rel='noopener noreferrer'>GitHub Sponsors</a>
&nbsp; &nbsp;<a href='https://ko-fi.com/davidjcralph' className='aboutLink' target='_blank' rel='noopener noreferrer'>Ko-Fi</a>
&nbsp; &nbsp;<a href='https://patreon.com/davidjcralph' className='aboutLink' target='_blank' rel='noopener noreferrer'>Patreon</a>
<a href={'https://github.com/sponsors/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>GitHub Sponsors</a>
&nbsp; &nbsp;<a href={'https://ko-fi.com/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Ko-Fi</a>
&nbsp; &nbsp;<a href={'https://patreon.com/' + window.constants.DONATE_USERNAME} className='aboutLink' target='_blank' rel='noopener noreferrer'>Patreon</a>
</p>
<h3>{this.language.resources_used.title}</h3>
@@ -131,7 +133,7 @@ export default class About extends React.PureComponent {
<p>{this.state.loading}</p>
{this.state.sponsors.map((item) => (
<Tooltip title={item.handle} key={item.handle}>
<a href={item.profile} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar + '&size=128'} alt={item.handle}></img></a>
<a href={'https://github.com/' + item.handle} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar + '&size=128'} alt={item.handle}></img></a>
</Tooltip>
))}

View File

@@ -5,12 +5,14 @@ import FileUpload from '../FileUpload';
import Text from '../Text';
import Switch from '../Switch';
import ResetModal from '../ResetModal';
import Dropdown from '../Dropdown';
import SettingsFunctions from '../../../../../modules/helpers/settings';
import SettingsFunctions from '../../../../../modules/helpers/settings/modals';
import { toast } from 'react-toastify';
import Modal from 'react-modal';
const time_zones = require('../../../../widgets/time/timezones.json');
export default class AdvancedSettings extends React.PureComponent {
constructor() {
super();
@@ -20,16 +22,6 @@ export default class AdvancedSettings extends React.PureComponent {
this.language = window.language.modals.main.settings;
}
settingsImport(e) {
const content = JSON.parse(e.target.result);
Object.keys(content).forEach((key) => {
localStorage.setItem(key, content[key]);
});
toast(window.language.toasts.imported);
}
render() {
const { advanced } = this.language.sections;
@@ -37,12 +29,18 @@ export default class AdvancedSettings extends React.PureComponent {
<>
<h2>{advanced.title}</h2>
<Checkbox name='offlineMode' text={advanced.offline_mode} element='.other' />
<Dropdown name='timezone' label={advanced.timezone.title} category='timezone'>
<option value='auto'>{advanced.timezone.automatic}</option>
{time_zones.map((timezone) => (
<option value={timezone} key={timezone}>{timezone}</option>
))}
</Dropdown>
<h3>{advanced.data}</h3>
<button className='reset' onClick={() => this.setState({ resetModal: true })}>{this.language.buttons.reset}</button>
<button className='export' onClick={() => SettingsFunctions.exportSettings()}>{this.language.buttons.export}</button>
<button className='import' onClick={() => document.getElementById('file-input').click()}>{this.language.buttons.import}</button>
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => this.settingsImport(e)}/>
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => SettingsFunctions.importSettings(e)}/>
<h3>{advanced.customisation}</h3>
<Text title={advanced.tab_name} name='tabName' default={window.language.tabname} category='other'/>
@@ -50,7 +48,7 @@ export default class AdvancedSettings extends React.PureComponent {
<Text title={advanced.custom_css} name='customcss' textarea={true} category='other'/>
<h3>{this.language.sections.experimental.title}</h3>
<p style={{ 'maxWidth': '75%'}}>{advanced.experimental_warning}</p>
<p style={{ maxWidth: '75%' }}>{advanced.experimental_warning}</p>
<Switch name='experimental' text={this.language.enabled} element='.other'/>
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ resetModal: false })} isOpen={this.state.resetModal} className='Modal resetmodal mainModal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>

View File

@@ -1,5 +1,3 @@
import { engineName } from 'react-device-detect';
import Checkbox from '../Checkbox';
import Dropdown from '../Dropdown';
import Radio from '../Radio';
@@ -30,8 +28,8 @@ export default function AppearanceSettings() {
<Radio name='theme' title={appearance.theme.title} options={themeOptions} category='other' />
<h3>{appearance.navbar.title}</h3>
<Checkbox name='notesEnabled' text={appearance.navbar.notes} element='.other' />
<Checkbox name='refresh' text={appearance.navbar.refresh} element='.other' />
<Checkbox name='notesEnabled' text={appearance.navbar.notes} category='navbar' />
<Checkbox name='refresh' text={appearance.navbar.refresh} category='navbar' />
<h3>{appearance.font.title}</h3>
<Text title={appearance.font.custom} name='font' upperCaseFirst={true} category='other' />
@@ -56,7 +54,7 @@ export default function AppearanceSettings() {
</Dropdown>
<h3>{appearance.accessibility.title}</h3>
{(engineName === 'Blink') ?
{(navigator.userAgent.includes('Chrome') && typeof InstallTrigger === 'undefined') ?
<Slider title={appearance.accessibility.widget_zoom} name='widgetzoom' default='100' step='10' min='50' max='200' display='%' category='other'/>
: null}
<Slider title={appearance.accessibility.toast_duration} name='toastDisplayTime' default='2500' step='100' min='500' max='5000' toast={true} display={' ' + appearance.accessibility.milliseconds} />

View File

@@ -25,7 +25,7 @@ export default class Changelog extends React.PureComponent {
return;
}
let date = new Date(data.date);
let date = new Date(data.date.split(' ')[0]);
date = date.toLocaleDateString(window.languagecode.replace('_', '-'), {
year: 'numeric',
month: 'long',
@@ -101,8 +101,8 @@ export default class Changelog extends React.PureComponent {
return (
<div className='changelogtab'>
<h1 style={{ 'marginBottom': '-10px' }}>{this.state.title}</h1>
<h5 style={{ 'lineHeight': '0px' }}>{this.state.author} {this.state.date}</h5>
<h1 style={{ marginBottom: '-10px' }}>{this.state.title}</h1>
<h5 style={{ lineHeight: '0px' }}>{this.state.author} {this.state.date}</h5>
{this.state.image ? <img draggable='false' src={this.state.image} alt={window.language.modals.update.title} className='updateimage'/> : null}
<div className='updatechangelog' dangerouslySetInnerHTML={{ __html: this.state.html }}/>
<Modal closeTimeoutMS={100} onRequestClose={() => this.setState({ showLightbox: false })} isOpen={this.state.showLightbox} className='Modal lightboxmodal' overlayClassName='Overlay resetoverlay' ariaHideApp={false}>

View File

@@ -1,5 +1,6 @@
import Checkbox from '../Checkbox';
import Slider from '../Slider';
import EventBus from '../../../../../modules/helpers/eventbus';
export default function ExperimentalSettings() {
const { experimental } = window.language.modals.main.settings.sections;
@@ -9,11 +10,21 @@ export default function ExperimentalSettings() {
<h2>{experimental.title}</h2>
<p>{experimental.warning}</p>
<Checkbox name='animations' text={window.language.modals.main.settings.sections.appearance.animations} element='.other'/>
<h3>Usage Stats</h3>
<p>Allows you to see stats such as how many tabs you have opened, quotes favourited etc. It also sends this data anonymously to our<a className='modalLink' href='https://github.com/mue/umami'>umami</a> instance.</p>
<Checkbox name='stats' text='Enable Usage Stats' element='.other'/>
<h3>{experimental.developer}</h3>
<Checkbox name='debug' text='Debug hotkey (Ctrl + #)' element='.other'/>
<Slider title='Debug timeout' name='debugtimeout' min='0' max='5000' default='0' step='100' display=' miliseconds' element='.other' />
<br/>
<p>Send Event</p>
Type <input type='text' id='eventType'/>
<br/><br/>
<button className='reset' style={{'marginLeft': '0px'}} onClick={() => localStorage.clear()}>Clear LocalStorage</button>
Name <input type='text' id='eventName'/>
<br/><br/>
<button className='uploadbg' onClick={() => EventBus.dispatch(document.getElementById('eventType').value, document.getElementById('eventName').value)}>Send</button>
<br/><br/>
<button className='reset' style={{ marginLeft: '0px' }} onClick={() => localStorage.clear()}>Clear LocalStorage</button>
</>
);
}

View File

@@ -5,9 +5,6 @@ import Switch from '../Switch';
import Text from '../Text';
import Slider from '../Slider';
import DayPickerInput from 'react-day-picker/DayPickerInput';
import 'react-day-picker/lib/style.css';
export default class GreetingSettings extends React.PureComponent {
constructor() {
super();
@@ -17,11 +14,11 @@ export default class GreetingSettings extends React.PureComponent {
this.language = window.language.modals.main.settings;
}
changeDate = (data) => {
localStorage.setItem('birthday', data);
changeDate = (e) => {
localStorage.setItem('birthday', e.target.value);
this.setState({
birthday: data
birthday: new Date(e.target.value)
});
}
@@ -41,7 +38,7 @@ export default class GreetingSettings extends React.PureComponent {
<Switch name='birthdayenabled' text={this.language.enabled} category='greeting' element='.greeting'/>
<Checkbox name='birthdayage' text={greeting.birthday_age} category='greeting' element='.greeting'/>
<p>{greeting.birthday_date}</p>
<DayPickerInput onDayChange={this.changeDate} value={this.state.birthday}/>
<input type='date' onChange={this.changeDate} value={this.state.birthday.toISOString().substr(0, 10)}/>
</>
);
}

View File

@@ -27,7 +27,7 @@ const widget_name = {
const SortableItem = sortableElement(({ value }) => (
<li className='sortableitem' style={{ display: enabled(value) ? 'block' : 'none' }}>
<DragHandleIcon style={{'verticalAlign': 'middle'}} />
<DragHandleIcon style={{ verticalAlign: 'middle' }} />
{widget_name[value]}
</li>
));
@@ -52,7 +52,6 @@ export default class OrderSettings extends React.PureComponent {
}
const newArray = [...array];
const target = newArray[oldIndex];
const inc = newIndex < oldIndex ? -1 : 1;
@@ -61,7 +60,6 @@ export default class OrderSettings extends React.PureComponent {
}
newArray[newIndex] = target;
return newArray;
}
@@ -83,6 +81,7 @@ export default class OrderSettings extends React.PureComponent {
componentDidUpdate() {
localStorage.setItem('order', JSON.stringify(this.state.items));
window.stats.postEvent('setting', 'Widget order');
EventBus.dispatch('refresh', 'widgets');
}

View File

@@ -1,5 +1,6 @@
import Switch from '../Switch';
import Checkbox from '../Checkbox';
import Slider from '../Slider';
export default function QuickLinks() {
const language = window.language.modals.main.settings.sections.quicklinks;
@@ -8,9 +9,10 @@ export default function QuickLinks() {
<>
<h2>{language.title}</h2>
<Switch name='quicklinksenabled' text={window.language.modals.main.settings.enabled} category='quicklinks' element='.quicklinks-container' />
<Checkbox name='quicklinksddgProxy' text={window.language.modals.main.settings.sections.background.ddg_proxy} element='.other' />
<Checkbox name='quicklinksddgProxy' text={window.language.modals.main.settings.sections.background.ddg_image_proxy} element='.other' />
<Checkbox name='quicklinksnewtab' text={language.open_new} category='quicklinks' />
<Checkbox name='quicklinkstooltip' text={language.tooltip} category='quicklinks' />
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuicklinks' min='10' max='400' default='100' display='%' category='quicklinks' element='.quicklinks-container' />
</>
);
}

View File

@@ -21,20 +21,32 @@ export default class QuoteSettings extends React.PureComponent {
}
render() {
const { quote } = window.language.modals.main.settings.sections;
const { quote, background } = window.language.modals.main.settings.sections;
let quoteSettings;
const customSettings = (
<>
<Text title={quote.custom} name='customQuote' category='quote' element='.quotediv' />
<Text title={quote.custom_author} name='customQuoteAuthor' category='quote' element='.quotediv'/>
</>
);
switch (this.state.quoteType) {
case 'custom': quoteSettings = customSettings; break;
default: break;
let customSettings;
if (this.state.quoteType === 'custom') {
customSettings = (
<>
<Text title={quote.custom} name='customQuote' category='quote' element='.quotediv' />
<Text title={quote.custom_author} name='customQuoteAuthor' category='quote' element='.quotediv'/>
</>
);
} else {
// api
customSettings = (
<>
<br/><br/>
<Dropdown label={background.interval.title} name='quotechange'>
<option value='refresh'>{window.language.tabname}</option>
<option value='60000'>{background.interval.minute}</option>
<option value='1800000'>{background.interval.half_hour}</option>
<option value='3600000'>{background.interval.hour}</option>
<option value='86400000'>{background.interval.day}</option>
<option value='604800000'>{window.language.widgets.date.week}</option>
<option value='2628000000'>{background.interval.month}</option>
</Dropdown>
</>
);
}
return (
@@ -47,7 +59,7 @@ export default class QuoteSettings extends React.PureComponent {
<option value='api'>{window.language.modals.main.settings.sections.background.type.api}</option>
<option value='custom'>{quote.custom}</option>
</Dropdown>
{quoteSettings}
{customSettings}
<Slider title={window.language.modals.main.settings.sections.appearance.accessibility.widget_zoom} name='zoomQuote' min='10' max='400' default='100' display='%' category='quote' element='.quotediv' />
<h3>{quote.buttons.title}</h3>

View File

@@ -7,11 +7,10 @@ import Radio from '../Radio';
import EventBus from '../../../../../modules/helpers/eventbus';
import { isChrome } from 'react-device-detect';
import { toast } from 'react-toastify';
const searchEngines = require('../../../../widgets/search/search_engines.json');
const autocompleteProviders = require('../../../../widgets/search/autocomplete_providers.json')
const autocompleteProviders = require('../../../../widgets/search/autocomplete_providers.json');
export default class SearchSettings extends React.PureComponent {
constructor() {
@@ -21,7 +20,6 @@ export default class SearchSettings extends React.PureComponent {
customDisplay: 'none',
customValue: localStorage.getItem('customSearchEngine') || ''
};
this.language = window.language.modals.main.settings;
}
resetSearch() {
@@ -30,7 +28,7 @@ export default class SearchSettings extends React.PureComponent {
customValue: ''
});
toast(window.language.modals.main.settings.toasts.reset);
toast(window.language.toasts.reset);
}
componentDidMount() {
@@ -77,17 +75,18 @@ export default class SearchSettings extends React.PureComponent {
<>
<h2>{search.title}</h2>
<Switch name='searchBar' text={language.enabled} category='widgets' />
{isChrome ? <Checkbox name='voiceSearch' text={search.voice_search} category='search' element='.other' /> : null}
{(navigator.userAgent.includes('Chrome') && typeof InstallTrigger === 'undefined') ?
<Checkbox name='voiceSearch' text={search.voice_search} category='search'/>
: null}
<Dropdown label={search.search_engine} name='searchEngine' onChange={(value) => this.setSearchEngine(value)}>
{searchEngines.map((engine) => (
<option key={engine.name} value={engine.settingsName}>{engine.name}</option>
))}
<option value='custom'>{search.custom.split(' ')[0]}</option>
</Dropdown>
<ul style={{ display: this.state.customDisplay }}>
<br/>
<p style={{ 'marginTop': '0px' }}>{search.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{language.buttons.reset}</span></p>
<p style={{ marginTop: '0px' }}>{search.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{language.buttons.reset}</span></p>
<input type='text' value={this.state.customValue} onInput={(e) => this.setState({ customValue: e.target.value })}></input>
</ul>
<br/>

View File

@@ -29,7 +29,7 @@ export default class TimeSettings extends React.PureComponent {
getAuto() {
navigator.geolocation.getCurrentPosition(async (position) => {
const data = await (await fetch(`${window.constants.WEATHER_URL}/location?getAuto=true&lat=${position.coords.latitude}&lon=${position.coords.longitude}`)).json();
const data = await (await fetch(`${window.constants.PROXY_URL}/weather/autolocation?lat=${position.coords.latitude}&lon=${position.coords.longitude}`)).json();
this.setState({
location: data[0].name
});

View File

@@ -114,7 +114,7 @@ export default class BackgroundSettings extends React.PureComponent {
const APISettings = (
<>
<br/>
<Radio title={background.source.api} options={apiOptions} name='backgroundAPI' category='background'/>
<Radio title={background.source.api} options={apiOptions} name='backgroundAPI' category='background' element='#backgroundImage'/>
<br/>
<Dropdown label={background.category} name='apiCategory'>
{this.state.backgroundCategories.map((category) => (
@@ -128,6 +128,16 @@ export default class BackgroundSettings extends React.PureComponent {
<option value='normal'>{background.source.quality.normal}</option>
<option value='datasaver'>{background.source.quality.datasaver}</option>
</Dropdown>
<br/><br/>
<Dropdown label={background.interval.title} name='backgroundchange'>
<option value='refresh'>{window.language.tabname}</option>
<option value='60000'>{background.interval.minute}</option>
<option value='1800000'>{background.interval.half_hour}</option>
<option value='3600000'>{background.interval.hour}</option>
<option value='86400000'>{background.interval.day}</option>
<option value='604800000'>{window.language.widgets.date.week}</option>
<option value='2628000000'>{background.interval.month}</option>
</Dropdown>
</>
);
@@ -148,12 +158,16 @@ export default class BackgroundSettings extends React.PureComponent {
case 'colour': backgroundSettings = <ColourSettings/>; break;
default: backgroundSettings = APISettings; break;
}
if (localStorage.getItem('photo_packs') && this.state.backgroundType !== 'custom' && this.state.backgroundType !== 'colour' && this.state.backgroundType !== 'api') {
backgroundSettings = null;
}
return (
<>
<h2>{background.title}</h2>
<Switch name='background' text={this.language.enabled} category='background' />
<Checkbox name='ddgProxy' text={background.ddg_proxy} />
<Switch name='background' text={this.language.enabled} category='background' element='#backgroundImage' />
<Checkbox name='ddgProxy' text={background.ddg_image_proxy} />
<Checkbox name='bgtransition' text={background.transition} />
<Checkbox name='photoInformation' text={background.photo_information} category='background' element='.other' />
@@ -169,22 +183,22 @@ export default class BackgroundSettings extends React.PureComponent {
{backgroundSettings}
<h3>{background.buttons.title}</h3>
<Checkbox name='view' text={background.buttons.view} element='.other' />
<Checkbox name='favouriteEnabled' text={background.buttons.favourite} element='.other' />
<Checkbox name='view' text={background.buttons.view} category='navbar' />
<Checkbox name='favouriteEnabled' text={background.buttons.favourite} category='navbar' />
<Checkbox name='downloadbtn' text={background.buttons.download} element='.other' />
<h3>{background.effects.title}</h3>
<Slider title={background.effects.blur} name='blur' min='0' max='100' default='0' display='%' category='background' />
<Slider title={background.effects.brightness} name='brightness' min='0' max='100' default='90' display='%' category='background' />
<Slider title={background.effects.blur} name='blur' min='0' max='100' default='0' display='%' category='background' element='#backgroundImage' />
<Slider title={background.effects.brightness} name='brightness' min='0' max='100' default='90' display='%' category='background' element='#backgroundImage' />
<br/><br/>
<Dropdown label={background.effects.filters.title} name='backgroundFilter' category='background'>
<Dropdown label={background.effects.filters.title} name='backgroundFilter' category='background' element='#backgroundImage'>
<option value='grayscale'>{background.effects.filters.grayscale}</option>
<option value='sepia'>{background.effects.filters.sepia}</option>
<option value='invert'>{background.effects.filters.invert}</option>
<option value='saturate'>{background.effects.filters.saturate}</option>
<option value='contrast'>{background.effects.filters.contrast}</option>
</Dropdown>
<Slider title={background.effects.filters.amount} name='backgroundFilterAmount' min='0' max='100' default='0' display='%' category='background' />
<Slider title={background.effects.filters.amount} name='backgroundFilterAmount' min='0' max='100' default='0' display='%' category='background' element='#backgroundImage' />
</>
);
}

View File

@@ -55,7 +55,7 @@ export default class ColourSettings extends React.PureComponent {
try {
gradientSettings = JSON.parse(hex);
} catch (e) {
// Disregard exception.
// Disregard exception
}
}
@@ -103,6 +103,8 @@ export default class ColourSettings extends React.PureComponent {
};
return newState;
});
window.stats.postEvent('setting', 'Changed backgroundtype from colour to gradient');
}
currentGradientSettings = () => {

View File

@@ -8,8 +8,8 @@ export default function Addons() {
return (
<Tabs>
<div label={addons.added}><Added/></div>
<div label={addons.sideload}><Sideload/></div>
<div label={addons.added} name='added'><Added/></div>
<div label={addons.sideload} name='sideload'><Sideload/></div>
</Tabs>
);
}

View File

@@ -7,9 +7,9 @@ export default function Marketplace() {
return (
<Tabs>
<div label={marketplace.photo_packs}><MarketplaceTab type='photo_packs'/></div>
<div label={marketplace.quote_packs}><MarketplaceTab type='quote_packs'/></div>
<div label={marketplace.preset_settings}><MarketplaceTab type='preset_settings'/></div>
<div label={marketplace.photo_packs} name='photo_packs'><MarketplaceTab type='photo_packs'/></div>
<div label={marketplace.quote_packs} name='quote_packs'><MarketplaceTab type='quote_packs'/></div>
<div label={marketplace.preset_settings} name='preset_settings'><MarketplaceTab type='preset_settings'/></div>
</Tabs>
);
}

View File

@@ -26,22 +26,22 @@ export default function Settings() {
return (
<>
<Tabs>
<div label={sections.time.title}><Time/></div>
<div label={sections.quote.title}><Quote/></div>
<div label={sections.greeting.title}><Greeting/></div>
<div label={sections.background.title}><Background/></div>
<div label={sections.search.title}><Search/></div>
<div label={sections.quicklinks.title}><QuickLinks/></div>
<div label={sections.weather.title}><Weather/></div>
<div label={sections.appearance.title}><Appearance/></div>
<div label={sections.order.title}><Order/></div>
<div label={sections.language.title}><Language/></div>
<div label={sections.advanced.title}><Advanced/></div>
<div label={sections.experimental.title}><Experimental/></div>
<div label={sections.changelog}><Changelog/></div>
<div label={sections.about.title}><About/></div>
<div label={sections.time.title} name='time'><Time/></div>
<div label={sections.quote.title} name='quote'><Quote/></div>
<div label={sections.greeting.title} name='greeting'><Greeting/></div>
<div label={sections.background.title} name='background'><Background/></div>
<div label={sections.search.title} name='search'><Search/></div>
<div label={sections.quicklinks.title} name='quicklinks'><QuickLinks/></div>
<div label={sections.weather.title} name='weather'><Weather/></div>
<div label={sections.appearance.title} name='appearance'><Appearance/></div>
<div label={sections.order.title} name='order'><Order/></div>
<div label={sections.language.title} name='language'><Language/></div>
<div label={sections.advanced.title} name='advanced'><Advanced/></div>
<div label={sections.experimental.title} name='experimental'><Experimental/></div>
<div label={sections.changelog} name='changelog'><Changelog/></div>
<div label={sections.about.title} name='about'><About/></div>
</Tabs>
<div className='reminder-info' style={{ 'display': display }}>
<div className='reminder-info' style={{ display: display }}>
<h1>{reminder.title}</h1>
<p>{reminder.message}</p>
<button className='pinNote' onClick={() => window.location.reload()}>{window.language.modals.main.error_boundary.refresh}</button>

View File

@@ -64,12 +64,14 @@ function Tab(props) {
case settings.changelog: icon = <Changelog/>; break;
case settings.about.title: icon = <About/>; break;
// Store
// Addons
case addons.added: icon = <Added/>; break;
case addons.sideload: icon = <Sideload/>; break;
// Marketplace
case marketplace.photo_packs: icon = <Background/>; break;
case marketplace.quote_packs: icon = <Quote/>; break;
case marketplace.preset_settings: icon = <Advanced/>; break;
case addons.added: icon = <Added/>; break;
case addons.sideload: icon = <Sideload/>; break;
default: break;
}
@@ -90,4 +92,4 @@ function Tab(props) {
);
}
export default React.memo(Tab);
export default React.memo(Tab);

View File

@@ -8,13 +8,19 @@ export default class Tabs extends React.PureComponent {
super(props);
this.state = {
currentTab: this.props.children[0].props.label
currentTab: this.props.children[0].props.label,
currentName: this.props.children[0].props.name
};
}
onClick = (tab) => {
onClick = (tab, name) => {
if (name !== this.state.currentName) {
window.stats.postEvent('tab', `Changed ${this.state.currentName} to ${name}`);
}
this.setState({
currentTab: tab
currentTab: tab,
currentName: name
});
};
@@ -28,7 +34,7 @@ export default class Tabs extends React.PureComponent {
tabClass = '';
optionsText = '';
}
return (
<>
<ul className={className}>
@@ -36,9 +42,9 @@ export default class Tabs extends React.PureComponent {
{this.props.children.map((tab, index) => (
<Tab
currentTab={this.state.currentTab}
key={tab.props.label || index}
key={index}
label={tab.props.label}
onClick={this.onClick}
onClick={(nextTab) => this.onClick(nextTab, tab.props.name)}
navbar={this.props.navbar || false}
/>
))}

View File

@@ -0,0 +1,16 @@
export default function ProgressBar(props) {
return (
<div className='progressbar'>
{props.count.map((num) => {
let className = 'step';
const index = props.count.indexOf(num);
if (index === props.currentTab) {
className = 'step active';
}
return <div className={className} key={index} onClick={() => props.switchTab(index)}></div>;
})}
</div>
);
}

View File

@@ -1,28 +1,103 @@
import EmailIcon from '@material-ui/icons/Email';
import TwitterIcon from '@material-ui/icons/Twitter';
import ForumIcon from '@material-ui/icons/Forum';
import React from 'react';
import EventBus from '../../../modules/helpers/eventbus';
import WelcomeSections from './WelcomeSections';
import ProgressBar from './ProgressBar';
import './welcome.scss';
export default function WelcomeModal(props) {
const language = window.language.modals.welcome;
export default class WelcomeModal extends React.PureComponent {
constructor() {
super();
this.state = {
image: './././icons/undraw_celebration.svg',
currentTab: 0,
finalTab: 4,
buttonText: window.language.modals.welcome.buttons.next
};
this.language = window.language.modals.welcome;
this.images = [
'./././icons/undraw_celebration.svg',
'./././icons/undraw_around_the_world_modified.svg',
'./././icons/undraw_add_files_modified.svg',
'./././icons/undraw_dark_mode.svg',
'./././icons/undraw_private_data_modified.svg',
'./././icons/undraw_upgrade_modified.svg'
];
}
return (
<div className='welcomeContent'>
<span className='closeModal' onClick={props.modalClose}>&times;</span>
<div className='welcomeModalText'>
<h2 className='subtitle'>{language.title}</h2>
<h1 className='welcometitle'>Mue Tab</h1>
<img alt='celebration' style={{ 'height': '200px', 'width': 'auto' }} draggable={false} src='./././icons/undraw_celebration.svg' />
<h2 className='subtitle'>{language.information}</h2>
<p>{language.thankyoumessage1}<br/> {language.thankyoumessage2}</p>
<h2 className='subtitle'>{language.support}</h2>
<a href='mailto:hello@muetab.com' className='welcomeLink' target='_blank' rel='noopener noreferrer'><EmailIcon/></a>
<a href='https://twitter.com/getmue' className='welcomeLink' target='_blank' rel='noopener noreferrer'><TwitterIcon/></a>
<a href='https://discord.gg/zv8C9F8' className='welcomeLink' target='_blank' rel='noopener noreferrer'><ForumIcon/></a>
<br/>
<button className='close' onClick={props.modalClose}>{language.close}</button>
changeTab(minus) {
localStorage.setItem('bgtransition', true);
localStorage.removeItem('welcomeTab');
if (minus) {
return this.setState({
currentTab: this.state.currentTab - 1,
image: this.images[this.state.currentTab - 1],
buttonText: this.language.buttons.next
});
}
if (this.state.buttonText === this.language.buttons.close) {
return this.props.modalClose();
}
this.setState({
currentTab: this.state.currentTab + 1,
image: this.images[this.state.currentTab + 1],
buttonText: (this.state.currentTab !== this.state.finalTab) ? this.language.buttons.next : this.language.buttons.close
});
}
// specific
switchTab(tab) {
this.setState({
currentTab: tab,
image: this.images[tab],
buttonText: (tab !== this.state.finalTab + 1) ? this.language.buttons.next : this.language.buttons.close
});
localStorage.setItem('bgtransition', true);
localStorage.removeItem('welcomeTab');
}
componentDidMount() {
const welcomeTab = localStorage.getItem('welcomeTab');
if (welcomeTab) {
this.setState({
currentTab: Number(welcomeTab),
image: this.images[Number(welcomeTab)],
buttonText: (Number(welcomeTab) !== this.state.finalTab + 1) ? this.language.buttons.next : this.language.buttons.close
});
}
EventBus.on('refresh', (data) => {
if (data === 'welcomeLanguage') {
localStorage.setItem('welcomeTab', this.state.currentTab);
localStorage.setItem('bgtransition', false);
window.location.reload();
}
});
}
render() {
return (
<div className='welcomeContent'>
<section>
<img className='showcaseimg' alt='celebration' draggable={false} src={this.state.image} />
<ProgressBar count={this.images} currentTab={this.state.currentTab} switchTab={(tab) => this.switchTab(tab)}/>
</section>
<section>
<div className='content'>
<WelcomeSections currentTab={this.state.currentTab} switchTab={(tab) => this.switchTab(tab)}/>
</div>
<div className='buttons'>
{(this.state.currentTab !== 0) ? <button className='close' style={{ marginRight: '20px' }} onClick={() => this.changeTab(true)}>{this.language.buttons.previous}</button> : null}
<button className='close' onClick={() => this.changeTab()}>{this.state.buttonText}</button>
</div>
</section>
</div>
</div>
);
);
}
}

View File

@@ -0,0 +1,217 @@
import React from 'react';
import Radio from '../main/settings/Radio';
import Checkbox from '../main/settings/Checkbox';
import FileUpload from '../main/settings/FileUpload';
import UploadIcon from '@material-ui/icons/CloudUpload';
import AutoIcon from '@material-ui/icons/AutoAwesome';
import LightModeIcon from '@material-ui/icons/LightMode';
import DarkModeIcon from '@material-ui/icons/DarkMode';
import SettingsFunctions from '../../../modules/helpers/settings';
import SettingsFunctionsModal from '../../../modules/helpers/settings/modals';
const languages = require('../../../modules/languages.json');
const default_settings = require('../../../modules/default_settings.json');
export default class WelcomeSections extends React.PureComponent {
constructor() {
super();
this.state = {
// themes
autoClass: 'toggle auto active',
lightClass: 'toggle lightTheme',
darkClass: 'toggle darkTheme',
// welcome
welcomeImage: 0,
// final
importedSettings: []
};
this.changeWelcomeImg = this.changeWelcomeImg.bind(this);
this.welcomeImages = ['./welcome-images/example1.webp', './welcome-images/example2.webp', './welcome-images/example3.webp', './welcome-images/example4.webp'];
}
changeTheme(type) {
this.setState({
autoClass: (type === 'auto') ? 'toggle auto active' : 'toggle auto',
lightClass: (type === 'light') ? 'toggle lightTheme active' : 'toggle lightTheme',
darkClass: (type === 'dark') ? 'toggle darkTheme active': 'toggle darkTheme'
});
localStorage.setItem('theme', type);
SettingsFunctions.loadSettings(true);
}
getSetting(name) {
const value = localStorage.getItem(name).replace('false', 'Off').replace('true', 'On');
return value.charAt(0).toUpperCase() + value.slice(1);
}
importSettings(e) {
SettingsFunctionsModal.importSettings(e);
let settings = [];
const data = JSON.parse(e.target.result);
Object.keys(data).forEach((setting) => {
if (setting === 'language' || setting === 'theme'|| setting === 'firstRun' || setting === 'showWelcome' || setting === 'showReminder') {
return;
}
const defaultSetting = default_settings.find((i) => i.name === setting);
if (defaultSetting !== undefined) {
if (data[setting] === String(defaultSetting.value)) {
return;
}
}
settings.push({
name: setting,
value: data[setting]
});
});
this.setState({
importedSettings: settings
});
this.props.switchTab(5);
}
changeWelcomeImg() {
let welcomeImage = this.state.welcomeImage;
this.setState({
welcomeImage: ++welcomeImage % this.welcomeImages.length
});
this.timeout = setTimeout(this.changeWelcomeImg, 3 * 1000);
}
componentDidMount() {
this.timeout = setTimeout(this.changeWelcomeImg, 3 * 1000);
}
// cancel welcome image timer if not on welcome tab
componentDidUpdate() {
if (this.props.currentTab !== 0) {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
} else {
if (!this.timeout) {
this.timeout = setTimeout(this.changeWelcomeImg, 3 * 1000);
}
}
}
render() {
const language = window.language.modals.welcome;
let tabContent;
const intro = (
<>
<h1>{language.sections.intro.title}</h1>
<p>{language.sections.intro.description}</p>
<h3 className='quicktip'>#shareyourmue</h3>
<div className='examples'>
<img src={this.welcomeImages[this.state.welcomeImage]} alt='Example Mue setup' draggable={false}/>
</div>
</>
);
const chooseLanguage = (
<>
<h1>{language.sections.language.title}</h1>
<p>{language.sections.language.description}</p>
<Radio name='language' options={languages} category='welcomeLanguage'/>
</>
);
const { appearance, advanced, background, quicklinks } = window.language.modals.main.settings.sections;
const languageSettings = window.language.modals.main.settings.sections.language;
const theme = (
<>
<h1>{language.sections.theme.title}</h1>
<p>{language.sections.theme.description}</p>
<div className='themesToggleArea'>
<div className={this.state.autoClass} onClick={() => this.changeTheme('auto')}>
<AutoIcon/>
<span>{appearance.theme.auto}</span>
</div>
<div className='options'>
<div className={this.state.lightClass} onClick={() => this.changeTheme('light')}>
<LightModeIcon/>
<span>{appearance.theme.light}</span>
</div>
<div className={this.state.darkClass} onClick={() => this.changeTheme('dark')}>
<DarkModeIcon/>
<span>{appearance.theme.dark}</span>
</div>
</div>
<h3 className='quicktip'>{language.tip}</h3>
<p>{language.sections.theme.tip}</p>
</div>
</>
);
const settings = (
<>
<h1>{language.sections.settings.title}</h1>
<p>{language.sections.settings.description}</p>
<button className='upload' onClick={() => document.getElementById('file-input').click()}>
<UploadIcon/>
<br/>
<span>{window.language.modals.main.settings.buttons.import}</span>
</button>
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => this.importSettings(e)}/>
<h3 className='quicktip'>{language.tip}</h3>
<p>{language.sections.settings.tip}</p>
</>
);
const privacy = (
<>
<h1>{language.sections.privacy.title}</h1>
<p>{language.sections.privacy.description}</p>
<Checkbox name='offlineMode' text={advanced.offline_mode} element='.other' />
<p>{language.sections.privacy.offline_mode_description}</p>
<Checkbox name='quicklinksddgProxy' text={background.ddg_image_proxy + ' (' + quicklinks.title + ')'}/>
<Checkbox name='ddgProxy' text={background.ddg_image_proxy + ' (' + background.title + ')'}/>
<p>{language.sections.privacy.ddg_proxy_description}</p>
<h3 className='quicktip'>{language.sections.privacy.links.title}</h3>
<a className='privacy' href={window.constants.PRIVACY_URL} target='_blank' rel='noopener noreferrer'>{language.sections.privacy.links.privacy_policy}</a>
<br/><br/>
<a className='privacy' href={'https://github.com/' + window.constants.ORG_NAME} target='_blank' rel='noopener noreferrer'>{language.sections.privacy.links.source_code}</a>
</>
);
const final = (
<>
<h1>{language.sections.final.title}</h1>
<p>{language.sections.final.description}</p>
<h3 className='quicktip'>{language.sections.final.changes}</h3>
<p>{language.sections.final.changes_description}</p>
<div className='themesToggleArea'>
<div className='toggle' onClick={() => this.props.switchTab(1)}><span>{languageSettings.title}: {languages.find((i) => i.value === localStorage.getItem('language')).name}</span></div>
<div className='toggle' onClick={() => this.props.switchTab(3)}><span>{appearance.theme.title}: {this.getSetting('theme')}</span></div>
{(this.state.importedSettings.length !== 0) ? <div className='toggle' onClick={() => this.props.switchTab(2)}>{language.sections.final.imported} {this.state.importedSettings.length} {language.sections.final.settings}</div> : null}
</div>
</>
);
switch (this.props.currentTab) {
case 1: tabContent = chooseLanguage; break;
case 2: tabContent = settings; break;
case 3: tabContent = theme; break;
case 4: tabContent = privacy; break;
case 5: tabContent = final; break;
// 0
default: tabContent = intro;
}
return tabContent;
}
}

View File

@@ -1,59 +1,176 @@
@import '../main/scss/index.scss';
.welcomemodal {
margin-top: 40px;
}
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: 80%;
width: 60%;
padding: 0;
.welcomeModalText {
line-height: 2px;
h2.subtitle {
font-size: 24px;
color: var(--modal-text);
text-transform: uppercase;
section {
width: 50%;
display: inline;
height: 80vh;
}
h1.welcometitle {
font-size: 50px;
section:nth-child(1) {
float: left;
background: var(--sidebar);
display: flex;
justify-content: center;
align-items: center;
}
section:nth-child(2) {
float: right;
.content {
padding: 20px;
}
.buttons {
position: absolute;
bottom: 2rem;
right: 2rem;
}
h1 {
font-size: 2.5rem;
}
h3.quicktip {
text-transform: uppercase;
color: #5352ed;
}
}
}
.welcomeContent {
margin-top: 2em;
.welcomeoverlay {
background-color: rgba(0, 0, 0, 0.4);
}
.progressbar {
position: fixed;
bottom: 50px;
text-align: center;
padding: 25px;
display: inline;
overflow: hidden;
white-space: nowrap;
a {
text-decoration: none;
line-height: 20px !important;
color: var(--modal-link);
.step {
display: inline-block;
width: 50px;
background: #8395a7;
height: 4px;
margin: 10px;
transition: .2s ease;
cursor: pointer;
border-radius: 15px;
}
img.icon,
svg {
margin-top: -12px;
padding: 10px;
cursor: pointer;
transition: ease 0.2s;
.active {
background: #5352ed;
}
}
.themesToggleArea {
.active {
background: var(--tab-active) !important;
}
.toggle {
background: var(--sidebar);
text-align: center;
border-radius: 40px;
padding: 20px;
margin: 10px;
&:hover {
transform: scale(1.1);
background: var(--tab-active);
cursor: pointer;
}
span {
font-size: 1rem;
}
}
p {
margin-top: 0.7rem;
line-height: 1em;
.auto {
svg {
font-size: 12px;
padding-right: 5px;
}
}
img,
svg {
height: 24px;
width: auto;
.options {
display: flex;
justify-content: space-between;
.lightTheme,
.darkTheme {
width: 40%;
padding: 50px;
span {
display: block;
}
}
}
}
.welcomeLink {
color: var(--modal-text) !important;
a.privacy {
text-decoration: none;
color: var(--modal-text);
font-size: 1rem;
&:hover {
color: #5352ed;
}
}
.examples {
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
img {
width: 30rem !important;
height: auto !important;
display: block;
margin: 30px;
border-radius: 12px;
}
}
.showcaseimg {
width: 350px;
height: auto;
}
@media only screen and (max-width: 1440px) {
.buttons {
position: relative !important;
bottom: 0rem !important;
}
.examples img {
width: 15rem !important;
}
}
@media only screen and (max-width: 1600px) {
.examples img {
width: 20rem !important;
}
.buttons {
position: relative !important;
bottom: 1rem !important;
}
}

View File

@@ -43,6 +43,11 @@ export default class Widgets extends React.PureComponent {
}
render() {
// don't show when welcome is there
if (localStorage.getItem('showWelcome') !== 'false') {
return <div id='widgets'></div>;
}
// allow for re-ordering widgets
let elements = [];
@@ -52,7 +57,7 @@ export default class Widgets extends React.PureComponent {
});
} else {
// prevent error
elements = ['greeting', 'time', 'quicklinks', 'quote', 'date'];
elements = [<Greeting/>, <Clock/>, <QuickLinks/>, <Quote/>, <Date/>];
}
return (
@@ -60,7 +65,7 @@ export default class Widgets extends React.PureComponent {
<React.Suspense fallback={renderLoader()}>
{this.enabled('searchBar') ? <Search/> : null}
{elements}
{this.enabled('weatherEnabled') && !localStorage.getItem('offlineMode') ? <Weather/> : null}
{this.enabled('weatherEnabled') && (localStorage.getItem('offlineMode') === 'false') ? <Weather/> : null}
</React.Suspense>
</div>
);

View File

@@ -1,7 +1,8 @@
// warning: the code here is fairly messy and probably needs a rewrite
// todo: rewrite this mess
import React from 'react';
import EventBus from '../../../modules/helpers/eventbus';
import Interval from '../../../modules/helpers/interval';
import PhotoInformation from './PhotoInformation';
@@ -55,20 +56,23 @@ export default class Background extends React.PureComponent {
Math.floor(Math.random() * offlineImages[photographer].photo.length)
];
this.setState({
const object = {
url: `./offline-images/${randomImage}.webp`,
photoInfo: {
offline: true,
credit: photographer
}
});
}
this.setState(object);
localStorage.setItem('currentBackground', JSON.stringify(object));
}
setBackground() {
const backgroundImage = document.getElementById('backgroundImage');
if (this.state.url !== '') {
const url = (localStorage.getItem('ddgProxy') === 'true' && this.state.photoInfo.offline !== true) ? window.constants.DDG_PROXY + this.state.url : this.state.url;
const url = (localStorage.getItem('ddgProxy') === 'true' && this.state.photoInfo.offline !== true && !this.state.url.startsWith('data:')) ? window.constants.DDG_IMAGE_PROXY + this.state.url : this.state.url;
const photoInformation = document.querySelector('.photoInformation');
// just set the background
@@ -98,7 +102,6 @@ export default class Background extends React.PureComponent {
backgroundImage.classList.remove('backgroundPreload');
backgroundImage.classList.add('fade-in');
// this doesn't make it fetch again which is nice
backgroundImage.style.background = `url(${url})`;
// remove the preloader element we created earlier
preloader.remove();
@@ -116,7 +119,10 @@ export default class Background extends React.PureComponent {
// Main background getting function
async getBackground() {
const offline = (localStorage.getItem('offlineMode') === 'true');
let offline = (localStorage.getItem('offlineMode') === 'true');
if (localStorage.getItem('showWelcome') === 'true') {
offline = true;
}
switch (localStorage.getItem('backgroundType')) {
case 'api':
@@ -132,8 +138,7 @@ export default class Background extends React.PureComponent {
photoInfo: {
credit: favourited.credit,
location: favourited.location,
camera: favourited.camera,
resolution: favourited.resolution
camera: favourited.camera
}
});
}
@@ -146,11 +151,10 @@ export default class Background extends React.PureComponent {
let requestURL, data;
switch (backgroundAPI) {
case 'unsplash':
//requestURL = `${window.constants.UNSPLASH_URL}/getImage?category=${apiCategory}`;
requestURL = `${window.constants.UNSPLASH_URL}/images/random?quality=${apiQuality}`;
requestURL = `${window.constants.PROXY_URL}/images/unsplash?quality=${apiQuality}`;
break;
case 'pexels':
requestURL = `${window.constants.PEXELS_URL}/images/random?quality=${apiQuality}`;
requestURL = `${window.constants.PROXY_URL}/images/pexels?quality=${apiQuality}`;
break;
// Defaults to Mue
default:
@@ -166,8 +170,7 @@ export default class Background extends React.PureComponent {
}
let credit = data.photographer;
let photoURL = '';
let photographerURL = '';
let photoURL, photographerURL;
if (backgroundAPI === 'unsplash') {
credit = data.photographer + ` ${this.language.unsplash}`;
@@ -179,7 +182,7 @@ export default class Background extends React.PureComponent {
photographerURL = data.photographer_page;
}
this.setState({
const object = {
url: data.file,
type: 'api',
currentAPI: backgroundAPI,
@@ -188,12 +191,14 @@ export default class Background extends React.PureComponent {
credit: credit,
location: data.location,
camera: data.camera,
resolution: data.resolution,
url: data.file,
photographerURL: photographerURL,
photoURL: photoURL
}
});
}
this.setState(object);
localStorage.setItem('currentBackground', JSON.stringify(object));
break;
case 'colour':
@@ -279,6 +284,10 @@ export default class Background extends React.PureComponent {
};
EventBus.on('refresh', (data) => {
if (data === 'welcomeLanguage') {
localStorage.setItem('welcomeImage', JSON.stringify(this.state));
}
if (data === 'background') {
if (localStorage.getItem('background') === 'false') {
// user is using custom colour or image
@@ -299,10 +308,11 @@ export default class Background extends React.PureComponent {
document.getElementById('backgroundVideo').style.display = 'block';
} else {
if (this.state.photoInfo.hidden === false) {
// fix bug
try {
document.querySelector('.photoInformation').style.display = 'block';
} catch (e) {}
} catch (e) {
// Disregard exception
}
}
element.style.display = 'block';
@@ -315,6 +325,10 @@ export default class Background extends React.PureComponent {
if (backgroundType !== this.state.type || (this.state.type === 'api' && localStorage.getItem('backgroundAPI') !== this.state.currentAPI) || (this.state.type === 'custom' && localStorage.getItem('customBackground') !== this.state.url)) {
return refresh();
}
} else {
if (backgroundType !== this.state.type) {
return refresh();
}
}
// background effects so we don't get another image again
@@ -328,12 +342,48 @@ export default class Background extends React.PureComponent {
}
// uninstall photo pack reverts your background to what you had previously
if (data === 'marketplacebackgrounduninstall') {
if (data === 'marketplacebackgrounduninstall' || data === 'backgroundwelcome') {
refresh();
}
});
this.getBackground();
if (localStorage.getItem('welcomeTab')) {
return this.setState(JSON.parse(localStorage.getItem('welcomeImage')));
}
const interval = localStorage.getItem('backgroundchange');
if (interval && interval !== 'refresh' && localStorage.getItem('backgroundType') === 'api') {
Interval(() => {
try {
document.getElementById('backgroundImage').classList.remove('fade-in');
document.getElementsByClassName('photoInformation')[0].classList.remove('fade-in');
} catch (e) {
// Disregard exception
}
this.getBackground();
}, Number(interval), 'background');
try {
// todo: refactor this mess
const current = JSON.parse(localStorage.getItem('currentBackground'));
const offline = localStorage.getItem('offlineMode')
if (current.url.startsWith('http') && offline === 'false') {
this.setState(current);
} else if (current.url.startsWith('http')) {
this.offlineBackground();
} else {
if (offline === 'false') {
localStorage.removeItem('currentBackground');
return this.getBackground();
}
this.setState(current);
}
} catch (e) {
this.setBackground();
}
} else {
this.getBackground();
}
}
// only set once we've got the info
@@ -352,7 +402,7 @@ export default class Background extends React.PureComponent {
};
return (
<video autoPlay muted={enabled('backgroundVideoMute')} loop={enabled('backgroundVideoLoop')} style={{ 'WebkitFilter': `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%)` }} id='backgroundVideo'>
<video autoPlay muted={enabled('backgroundVideoMute')} loop={enabled('backgroundVideoLoop')} style={{ WebkitFilter: `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%)` }} id='backgroundVideo'>
<source src={this.state.url}/>
</video>
);
@@ -362,9 +412,9 @@ export default class Background extends React.PureComponent {
return (
<>
<div style={{ 'WebkitFilter': `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilter + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}` }} id='backgroundImage'/>
<div style={{ WebkitFilter: `blur(${localStorage.getItem('blur')}px) brightness(${localStorage.getItem('brightness')}%) ${backgroundFilter ? backgroundFilter + '(' + localStorage.getItem('backgroundFilterAmount') + '%)' : ''}` }} id='backgroundImage'/>
{(this.state.photoInfo.credit !== '') ?
<PhotoInformation className={this.props.photoInformationClass} info={this.state.photoInfo} api={this.state.currentAPI}/>
<PhotoInformation className={this.props.photoInformationClass} info={this.state.photoInfo} api={this.state.currentAPI} url={this.state.url}/>
: null}
</>
);

View File

@@ -6,19 +6,25 @@ import StarIcon from '@material-ui/icons/Star';
import StarIcon2 from '@material-ui/icons/StarBorder';
export default class Favourite extends React.PureComponent {
buttons = {
favourited: <StarIcon onClick={() => this.favourite()} className='topicons' />,
unfavourited: <StarIcon2 onClick={() => this.favourite()} className='topicons' />
}
constructor() {
super();
this.state = {
favourited: (localStorage.getItem('favourite')) ? <StarIcon onClick={this.favourite} className='topicons' /> : <StarIcon2 onClick={this.favourite} className='topicons' />
favourited: (localStorage.getItem('favourite')) ? this.buttons.favourited : this.buttons.unfavourited
};
}
favourite = () => {
favourite() {
if (localStorage.getItem('favourite')) {
localStorage.removeItem('favourite');
this.setState({
favourited: <StarIcon2 onClick={this.favourite} className='topicons' />
favourited: this.buttons.unfavourited
});
window.stats.postEvent('feature', 'Background favourite');
} else {
const url = document.getElementById('backgroundImage').style.backgroundImage.replace('url("', '').replace('")', '');
@@ -30,13 +36,13 @@ export default class Favourite extends React.PureComponent {
url: url,
credit: document.getElementById('credit').textContent,
location: document.getElementById('infoLocation').textContent,
camera: document.getElementById('infoCamera').textContent,
resolution: document.getElementById('infoResolution').textContent
camera: document.getElementById('infoCamera').textContent
}));
this.setState({
favourited: <StarIcon onClick={this.favourite} className='topicons' />
favourited: this.buttons.favourited
});
window.stats.postEvent('feature', 'Background unfavourite');
}
}

View File

@@ -13,6 +13,12 @@ export default class Maximise extends React.PureComponent {
}
setAttribute(blur, brightness, filter) {
// don't attempt to modify the background if it isn't an image
const backgroundType = localStorage.getItem('backgroundType');
if (backgroundType === 'colour') {
return;
}
const element = document.getElementById('backgroundImage');
let backgroundFilter;
@@ -27,16 +33,9 @@ export default class Maximise extends React.PureComponent {
}
maximise = () => {
// elements to hide
const elements = ['.searchBar', '.clock', '.greeting', '.quotediv', 'time', '.quicklinks-container', '.weather', '.date'];
elements.forEach((element) => {
try {
(this.state.hidden === false) ? document.querySelector(element).style.display = 'none' : document.querySelector(element).style.display = 'block';
} catch (e) {
return;
}
});
// hide widgets
const widgets = document.getElementById('widgets');
(this.state.hidden === false) ? widgets.style.display = 'none' : widgets.style.display = 'block';
if (this.state.hidden === false) {
this.setState({
@@ -44,12 +43,14 @@ export default class Maximise extends React.PureComponent {
});
this.setAttribute(0, 100);
window.stats.postEvent('feature', 'Background maximise');
} else {
this.setState({
hidden: false
});
this.setAttribute(localStorage.getItem('blur'), localStorage.getItem('brightness'), true);
window.stats.postEvent('feature', 'Background unmaximise');
}
}

View File

@@ -1,3 +1,5 @@
import React from 'react';
import Info from '@material-ui/icons/Info';
import Location from '@material-ui/icons/LocationOn';
import Camera from '@material-ui/icons/PhotoCamera';
@@ -18,9 +20,13 @@ const downloadImage = async (info) => {
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.stats.postEvent('feature', 'Background download');
};
export default function PhotoInformation(props) {
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const language = window.language.widgets.background;
if (props.info.hidden === true || !props.info.credit) {
@@ -44,10 +50,20 @@ export default function PhotoInformation(props) {
}
}
// get resolution
const img = new Image();
img.onload = (event) => {
setWidth(event.target.width);
setHeight(event.target.height);
}
img.src = (localStorage.getItem('ddgProxy') === 'true' && !props.info.offline && !props.url.startsWith('data:')) ? window.constants.DDG_IMAGE_PROXY + props.url : props.url;
return (
<div className='photoInformation'>
<h1>{photo} <span id='credit'>{credit}</span></h1>
{localStorage.getItem('photoInformation') !== 'false' ? <><Info className='photoInformationHover'/>
{localStorage.getItem('photoInformation') !== 'false' ?
<>
<Info className='photoInformationHover'/>
<div className={props.className || 'infoCard'}>
<Info className='infoIcon'/>
<h1>{language.information}</h1>
@@ -57,7 +73,7 @@ export default function PhotoInformation(props) {
<Camera/>
<span id='infoCamera'>{props.info.camera || 'N/A'}</span>
<Resolution/>
<span id='infoResolution'>{props.info.resolution || 'N/A'}</span>
<span id='infoResolution'>{width}x{height}</span>
<Photographer/>
<span>{photographer}</span>
{(localStorage.getItem('downloadbtn') === 'true') && !props.info.offline && !props.info.photographerURL ?
@@ -66,7 +82,7 @@ export default function PhotoInformation(props) {
<span className='download' onClick={() => downloadImage(props.info)}>{language.download}</span>
</> : null}
</div>
</>: null}
</> : null}
</div>
);
}

View File

@@ -44,7 +44,7 @@
left: 0.7em;
padding: 1rem;
border-radius: 24px 24px 24px 0;
max-width: 500px;
width: 300px !important;
text-align: left;
text-shadow: none;

View File

@@ -1,6 +1,8 @@
import React from 'react';
import { utcToZonedTime } from 'date-fns-tz';
import EventBus from '../../../modules/helpers/eventbus';
import dtf from '../../../modules/helpers/date';
import './greeting.scss';
@@ -46,7 +48,12 @@ export default class Greeting extends React.PureComponent {
getGreeting(time = (60000 - Date.now() % 60000)) {
this.timer = setTimeout(() => {
const now = new Date();
let now = new Date();
const timezone = localStorage.getItem('timezone');
if (timezone) {
now = utcToZonedTime(now, timezone);
}
const hour = now.getHours();
// Set the default greeting string to "Good evening"
@@ -103,7 +110,7 @@ export default class Greeting extends React.PureComponent {
componentDidMount() {
EventBus.on('refresh', (data) => {
if (data === 'greeting') {
if (data === 'greeting' || data === 'timezone') {
const element = document.querySelector('.greeting');
if (localStorage.getItem('greeting') === 'false') {
@@ -114,7 +121,7 @@ export default class Greeting extends React.PureComponent {
this.getGreeting(0);
element.style.display = 'block';
element.style.fontSize = `${1.6 * Number(localStorage.getItem('zoomGreeting') / 100)}em`;
element.style.fontSize = `${1.6 * Number((localStorage.getItem('zoomGreeting') || 100) / 100)}em`;
}
});
@@ -125,6 +132,10 @@ export default class Greeting extends React.PureComponent {
this.getGreeting(0);
}
componentWillUnmount() {
EventBus.remove('refresh');
}
render() {
return <h1 className='greeting'>
{this.state.greeting}

View File

@@ -10,38 +10,54 @@ import Maximise from '../background/Maximise';
import Favourite from '../background/Favourite';
import Tooltip from '../../helpers/tooltip/Tooltip';
import EventBus from '../../../modules/helpers/eventbus';
import './scss/index.scss';
export default function Navbar(props) {
const backgroundEnabled = (localStorage.getItem('background') === 'true');
export default class Navbar extends React.PureComponent {
componentDidMount() {
EventBus.on('refresh', (data) => {
if (data === 'navbar') {
this.forceUpdate();
}
});
}
return (
<div className='navbar-container'>
{(localStorage.getItem('view') === 'true' && backgroundEnabled) ? <Maximise/> : null}
{(localStorage.getItem('favouriteEnabled') === 'true' && backgroundEnabled) ? <Favourite/> : null}
render() {
if (localStorage.getItem('showWelcome') !== 'false') {
return null;
}
{(localStorage.getItem('notesEnabled') === 'true') ?
<div className='notes'>
<NotesIcon className='topicons'/>
<Notes/>
</div>
: null}
{(window.constants.BETA_VERSION === true) ?
<Tooltip title={window.language.widgets.navbar.tooltips.feedback}>
<Report className='topicons' onClick={() => props.openModal('feedbackModal')}/>
const backgroundEnabled = (localStorage.getItem('background') === 'true');
return (
<div className='navbar-container'>
{(localStorage.getItem('view') === 'true' && backgroundEnabled) ? <Maximise/> : null}
{(localStorage.getItem('favouriteEnabled') === 'true' && backgroundEnabled) ? <Favourite/> : null}
{(localStorage.getItem('notesEnabled') === 'true') ?
<div className='notes'>
<NotesIcon className='topicons'/>
<Notes/>
</div>
: null}
{(window.constants.BETA_VERSION === true) ?
<Tooltip title={window.language.widgets.navbar.tooltips.feedback}>
<Report className='topicons' onClick={() => this.props.openModal('feedbackModal')}/>
</Tooltip>
: null}
{(localStorage.getItem('refresh') === 'true') ?
<Tooltip title={window.language.widgets.navbar.tooltips.refresh}>
<RefreshIcon className='refreshicon topicons' onClick={() => window.location.reload()}/>
</Tooltip>
: null}
<Tooltip title={window.language.modals.main.navbar.settings}>
<Gear className='settings-icon topicons' onClick={() => this.props.openModal('mainModal')}/>
</Tooltip>
: null}
{(localStorage.getItem('refresh') === 'true') ?
<Tooltip title={window.language.widgets.navbar.tooltips.refresh}>
<RefreshIcon className='refreshicon topicons' onClick={() => window.location.reload()}/>
</Tooltip>
: null}
<Tooltip title={window.language.modals.main.navbar.settings}>
<Gear className='settings-icon topicons' onClick={() => props.openModal('mainModal')}/>
</Tooltip>
</div>
);
</div>
);
}
}

View File

@@ -4,7 +4,7 @@ import TextareaAutosize from '@material-ui/core/TextareaAutosize';
import CopyIcon from '@material-ui/icons/FileCopyRounded';
import NotesIcon from '@material-ui/icons/AssignmentRounded';
import Pin from './Pin';
import Pin from '@material-ui/icons/PushPin';
import { toast } from 'react-toastify';
@@ -12,7 +12,8 @@ export default class Notes extends React.PureComponent {
constructor() {
super();
this.state = {
notes: localStorage.getItem('notes') || ''
notes: localStorage.getItem('notes') || '',
visibility: (localStorage.getItem('notesPinned') === 'true') ? 'visible' : 'hidden'
};
this.language = window.language.widgets.navbar.notes;
}
@@ -25,43 +26,43 @@ export default class Notes extends React.PureComponent {
};
pin() {
document.getElementById('noteContainer').classList.toggle('visibilityshow');
window.stats.postEvent('feature', 'Notes pin');
if (localStorage.getItem('notesPinned') === 'true') {
localStorage.setItem('notesPinned', false);
this.setState({
visibility: 'hidden'
});
} else {
localStorage.setItem('notesPinned', true);
this.setState({
visibility: 'visible'
});
}
}
copy() {
// this.state.notes doesnt work for some reason
navigator.clipboard.writeText(localStorage.getItem('notes'));
window.stats.postEvent('feature', 'Notes copied');
navigator.clipboard.writeText(this.state.notes);
toast(window.language.toasts.notes);
}
componentDidMount() {
const noteContainer = document.getElementById('noteContainer');
if (localStorage.getItem('notesPinned') === 'true') {
noteContainer.classList.toggle('visibilityshow');
}
if (localStorage.getItem('refresh') === 'false') {
noteContainer.style.marginLeft = '-200px';
document.getElementById('noteContainer').style.marginLeft = '-200px';
}
}
render() {
return (
<span id='noteContainer' className='notescontainer'>
<span id='noteContainer' className='notescontainer' style={{ visibility: this.state.visibility }}>
<div className='topbarnotes'>
<NotesIcon/>
<h3>{this.language.title}</h3>
</div>
<TextareaAutosize rowsMax={50} placeholder={this.language.placeholder} value={this.state.notes} onChange={this.setNotes}/>
<button onClick={this.pin} className='pinNote'><Pin/></button>
<button onClick={this.copy} className='copyNote'><CopyIcon/></button>
<TextareaAutosize rowsmax={50} placeholder={this.language.placeholder} value={this.state.notes} onChange={this.setNotes}/>
<button onClick={() => this.pin()} className='pinNote'><Pin/></button>
<button onClick={() => this.copy()} className='copyNote'><CopyIcon/></button>
</span>
);
}

View File

@@ -1,24 +0,0 @@
// License for original pin SVG below
/*
Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export default function Pin() {
return (
<svg xmlns='http://www.w3.org/2000/svg' enableBackground='new 0 0 24 24' viewBox='0 0 24 24' fill='black' width='18px' height='18px'>
<g><rect fill='none' height='24' width='24'/></g>
<g><path d='M16,9V4l1,0c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H7C6.45,2,6,2.45,6,3v0 c0,0.55,0.45,1,1,1l1,0v5c0,1.66-1.34,3-3,3h0v2h5.97v7l1,1l1-1v-7H19v-2h0C17.34,12,16,10.66,16,9z' fillRule='evenodd'/></g>
</svg>
);
}

View File

@@ -9,7 +9,7 @@
}
&:hover .notescontainer {
visibility: visible;
visibility: visible !important;
}
}

View File

@@ -32,7 +32,3 @@
}
}
}
.visibilityshow {
visibility: visible !important;
}

View File

@@ -31,6 +31,8 @@ export default class QuickLinks extends React.PureComponent {
this.setState({
items: data
});
window.stats.postEvent('feature', 'Quicklink delete');
}
addLink = () => {
@@ -73,7 +75,16 @@ export default class QuickLinks extends React.PureComponent {
url: ''
});
window.stats.postEvent('feature', 'Quicklink add');
this.toggleAdd();
// make sure image is correct size
const element = document.querySelector('.quicklinks-container');
const images = element.getElementsByTagName('img');
for (const img of images) {
img.style.height = `${0.87 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
};
}
toggleAdd = () => {
@@ -88,7 +99,20 @@ export default class QuickLinks extends React.PureComponent {
}
}
// widget zoom
setZoom(element) {
const images = element.getElementsByTagName('img');
for (const img of images) {
img.style.height = `${0.87 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
};
element.querySelector('button').style.fontSize = `${1.15 * Number((localStorage.getItem('zoomQuicklinks') || 100) / 100)}em`;
}
componentDidMount() {
if (localStorage.getItem('offlineMode') === 'true') {
return;
}
EventBus.on('refresh', (data) => {
if (data === 'quicklinks') {
const element = document.querySelector('.quicklinks-container');
@@ -98,6 +122,8 @@ export default class QuickLinks extends React.PureComponent {
}
element.style.display = 'block';
this.setZoom(element);
this.setState({
items: JSON.parse(localStorage.getItem('quicklinks'))
});
@@ -113,9 +139,19 @@ export default class QuickLinks extends React.PureComponent {
e.preventDefault();
}
};
this.setZoom(document.querySelector('.quicklinks-container'));
}
componentWillUnmount() {
EventBus.remove('refresh');
}
render() {
if (localStorage.getItem('offlineMode') === 'true') {
return null;
}
let target, rel = null;
if (localStorage.getItem('quicklinksnewtab') === 'true') {
target = '_blank';
@@ -147,12 +183,12 @@ export default class QuickLinks extends React.PureComponent {
quickLink(item)
))}
<button className='quicklinks' onClick={this.toggleAdd}>+</button>
<span className='quicklinkscontainer' style={{ 'visibility': this.state.showAddLink }}>
<span className='quicklinkscontainer' style={{ visibility: this.state.showAddLink }}>
<div className='topbarquicklinks'>
<h4>{this.language.new}</h4>
<TextareaAutosize rowsMax={1} placeholder={this.language.name} value={this.state.name} onChange={(e) => this.setState({ name: e.target.value })} />
<TextareaAutosize rowsmax={1} placeholder={this.language.name} value={this.state.name} onChange={(e) => this.setState({ name: e.target.value })} />
<p>{this.state.nameError}</p>
<TextareaAutosize rowsMax={10} placeholder={this.language.url} value={this.state.url} onChange={(e) => this.setState({ url: e.target.value })} />
<TextareaAutosize rowsmax={10} placeholder={this.language.url} value={this.state.url} onChange={(e) => this.setState({ url: e.target.value })} />
<p>{this.state.urlError}</p>
<button className='pinNote' onClick={this.addLink}>{this.language.add}</button>
</div>
@@ -160,4 +196,4 @@ export default class QuickLinks extends React.PureComponent {
</div>
);
}
}
}

View File

@@ -23,6 +23,7 @@
position: absolute;
margin-left: -140px;
margin-top: 50px;
z-index: 1;
svg {
float: left;

View File

@@ -1,6 +1,7 @@
import React from 'react';
import EventBus from '../../../modules/helpers/eventbus';
import Interval from '../../../modules/helpers/interval';
import FileCopy from '@material-ui/icons/FilterNone';
import TwitterIcon from '@material-ui/icons/Twitter';
@@ -12,23 +13,35 @@ import { toast } from 'react-toastify';
import './quote.scss';
export default class Quote extends React.PureComponent {
buttons = {
tweet: <TwitterIcon className='copyButton' onClick={() => this.tweetQuote()} />,
copy: <FileCopy className='copyButton' onClick={() => this.copyQuote()} />,
unfavourited: <StarIcon2 className='copyButton' onClick={() => this.favourite()} />,
favourited: <StarIcon className='copyButton' onClick={() => this.favourite()} />
}
constructor() {
super();
this.state = {
quote: '',
author: '',
favourited: <StarIcon2 className='copyButton' onClick={this.favourite} />,
tweet: '',
copy: '',
quoteLanguage: ''
};
this.buttons = {
tweet: <TwitterIcon className='copyButton' onClick={this.tweetQuote} />,
copy: <FileCopy className='copyButton' onClick={this.copyQuote} />
favourited: this.useFavourite(),
tweet: (localStorage.getItem('tweetButton') === 'false') ? null : this.buttons.tweet,
copy: (localStorage.getItem('copyButton') === 'false') ? null : this.buttons.copy,
quoteLanguage: '',
type: localStorage.getItem('quoteType') || 'api'
};
this.language = window.language.widgets.quote;
}
useFavourite() {
let favouriteQuote = null;
if (localStorage.getItem('favouriteQuoteEnabled') === 'true') {
favouriteQuote = localStorage.getItem('favouriteQuote') ? this.buttons.favourited : this.buttons.unfavourited;
}
return favouriteQuote;
}
doOffline() {
const quotes = require('./offline_quotes.json');
@@ -63,7 +76,7 @@ export default class Quote extends React.PureComponent {
});
}
switch (localStorage.getItem('quoteType') || 'api') {
switch (this.state.type) {
case 'custom':
const customQuote = localStorage.getItem('customQuote');
const customQuoteAuthor = localStorage.getItem('customQuoteAuthor');
@@ -72,8 +85,7 @@ export default class Quote extends React.PureComponent {
return this.setState({
quote: '"' + customQuote + '"',
author: customQuoteAuthor,
authorlink: this.getAuthorLink(customQuote),
type: 'custom'
authorlink: this.getAuthorLink(customQuote)
});
}
break;
@@ -91,8 +103,7 @@ export default class Quote extends React.PureComponent {
return this.setState({
quote: '"' + data[quotePackAPI.quote] + '"',
author: author,
authorlink: this.getAuthorLink(author),
type: 'quote_pack'
authorlink: this.getAuthorLink(author)
});
} catch (e) {
return this.doOffline();
@@ -109,8 +120,7 @@ export default class Quote extends React.PureComponent {
return this.setState({
quote: '"' + data.quote + '"',
author: data.author,
authorlink: this.getAuthorLink(data.author),
type: 'quote_pack'
authorlink: this.getAuthorLink(data.author)
});
} else {
return this.doOffline();
@@ -132,13 +142,15 @@ export default class Quote extends React.PureComponent {
return this.doOffline();
}
this.setState({
const object = {
quote: '"' + data.quote + '"',
author: data.author,
authorlink: this.getAuthorLink(data.author),
quoteLanguage: quotelanguage,
type: 'api'
});
quoteLanguage: quotelanguage
}
this.setState(object);
localStorage.setItem('currentQuote', JSON.stringify(object));
} catch (e) {
// ..and if that fails we load one locally
this.doOffline();
@@ -149,40 +161,35 @@ export default class Quote extends React.PureComponent {
}
}
copyQuote = () => {
copyQuote() {
window.stats.postEvent('feature', 'Quote copied');
navigator.clipboard.writeText(`${this.state.quote} - ${this.state.author}`);
toast(window.language.toasts.quote);
}
tweetQuote = () => {
tweetQuote() {
window.stats.postEvent('feature', 'Quote tweet');
window.open(`https://twitter.com/intent/tweet?text=${this.state.quote} - ${this.state.author} on @getmue`, '_blank').focus();
}
favourite = () => {
favourite() {
if (localStorage.getItem('favouriteQuote')) {
localStorage.removeItem('favouriteQuote');
this.setState({
favourited: <StarIcon2 className='copyButton' onClick={this.favourite} />
favourited: this.buttons.unfavourited
});
} else {
localStorage.setItem('favouriteQuote', this.state.quote + ' - ' + this.state.author);
this.setState({
favourited: <StarIcon className='copyButton' onClick={this.favourite} />
favourited: this.buttons.favourited
});
}
window.stats.postEvent('feature', 'Quote favourite');
}
init() {
let favouriteQuote = '';
if (localStorage.getItem('favouriteQuoteEnabled') === 'true') {
favouriteQuote = localStorage.getItem('favouriteQuote') ? <StarIcon className='copyButton' onClick={this.favourite} /> : <StarIcon2 className='copyButton' onClick={this.favourite} />;
}
this.setState({
favourited: favouriteQuote,
copy: (localStorage.getItem('copyButton') === 'false') ? null : this.buttons.copy,
tweet: (localStorage.getItem('tweetButton') === 'false') ? null : this.buttons.tweet
});
this.setZoom();
const quoteType = localStorage.getItem('quoteType');
@@ -192,6 +199,11 @@ export default class Quote extends React.PureComponent {
}
}
setZoom() {
document.querySelector('.quote').style.fontSize = `${0.8 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
}
componentDidMount() {
EventBus.on('refresh', (data) => {
if (data === 'quote') {
@@ -202,8 +214,6 @@ export default class Quote extends React.PureComponent {
}
element.style.display = 'block';
document.querySelector('.quote').style.fontSize = `${0.8 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
this.init();
}
@@ -212,17 +222,35 @@ export default class Quote extends React.PureComponent {
this.init();
}
});
document.querySelector('.quote').style.fontSize = `${0.8 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
document.querySelector('.quoteauthor').style.fontSize = `${0.9 * Number((localStorage.getItem('zoomQuote') || 100) / 100)}em`;
this.init();
const interval = localStorage.getItem('quotechange');
if (interval && interval !== 'refresh' && localStorage.getItem('quoteType') === 'api') {
Interval(() => {
this.setZoom();
this.getQuote();
}, Number(interval), 'quote');
try {
this.setState(JSON.parse(localStorage.getItem('currentQuote')));
} catch (e) {
this.setZoom();
this.getQuote();
}
} else {
// don't bother with the checks if we're loading for the first time
this.setZoom();
this.getQuote();
}
}
componentWillUnmount() {
EventBus.remove('refresh');
}
render() {
return (
<div className='quotediv'>
<h1 className='quote'>{`${this.state.quote}`}</h1>
<h1 className='quote'>{this.state.quote}</h1>
<h1 className='quoteauthor'>
<a href={this.state.authorlink} className='quoteauthorlink' target='_blank' rel='noopener noreferrer'>{this.state.author}</a>
<br/>

View File

@@ -1,7 +1,6 @@
import React from 'react';
import EventBus from '../../../modules/helpers/eventbus';
import fetchJSONP from 'fetch-jsonp';
import AutocompleteInput from '../../helpers/autocomplete/Autocomplete';
@@ -44,6 +43,7 @@ export default class Search extends React.PureComponent {
}
setTimeout(() => {
window.stats.postEvent('feature', 'Voice search');
window.location.href = this.state.url + `?${this.state.query}=` + searchText.value;
}, 1000);
};
@@ -58,23 +58,33 @@ export default class Search extends React.PureComponent {
value = document.getElementById('searchtext').value || 'mue fast';
}
window.stats.postEvent('feature', 'Search');
window.location.href = this.state.url + `?${this.state.query}=` + value;
}
async getSuggestions(input) {
const data = await (await fetchJSONP(this.state.autocompleteURL + this.state.autocompleteQuery + input, {
jsonpCallback: this.state.autocompleteCallback
})).json();
window.setResults = (results) => {
window.searchResults = results;
};
this.setState({
suggestions: data[1].splice(0, 3)
});
const script = document.createElement('script');
script.src = `${this.state.autocompleteURL + this.state.autocompleteQuery + input}&${this.state.autocompleteCallback}=window.setResults`;
document.head.appendChild(script);
try {
this.setState({
suggestions: window.searchResults[1].splice(0, 3)
});
} catch (e) {
// ignore error if empty
}
document.head.removeChild(script);
}
init() {
let url;
let url, microphone;
let query = 'q';
let microphone = null;
const setting = localStorage.getItem('searchEngine');
const info = searchEngines.find((i) => i.settingsName === setting);
@@ -94,9 +104,7 @@ export default class Search extends React.PureComponent {
microphone = <MicIcon className='micIcon' onClick={this.startSpeechRecognition}/>;
}
let autocompleteURL;
let autocompleteQuery;
let autocompleteCallback;
let autocompleteURL, autocompleteQuery, autocompleteCallback;
if (localStorage.getItem('autocomplete') === 'true') {
const info = autocompleteProviders.find((i) => i.value === localStorage.getItem('autocompleteProvider'));
@@ -134,7 +142,7 @@ export default class Search extends React.PureComponent {
<form action={this.state.url} className='searchBar'>
{this.state.microphone}
<SearchIcon onClick={this.searchButton}/>
<AutocompleteInput placeholder={this.language} name={this.state.query} id='searchtext' suggestions={this.state.suggestions} onChange={(e) => this.getSuggestions(e)} onClick={this.searchButton}/>
<AutocompleteInput placeholder={this.language} id='searchtext' suggestions={this.state.suggestions} onChange={(e) => this.getSuggestions(e)} onClick={this.searchButton}/>
</form>
);
}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { utcToZonedTime } from 'date-fns-tz';
import EventBus from '../../../modules/helpers/eventbus';
import './clock.scss';
@@ -20,11 +21,13 @@ export default class Clock extends React.PureComponent {
startTime(time = localStorage.getItem('seconds') === 'true' || localStorage.getItem('timeType') === 'analogue' ? (1000 - Date.now() % 1000) : (60000 - Date.now() % 60000)) {
this.timer = setTimeout(() => {
const now = new Date();
let now = new Date();
const timezone = localStorage.getItem('timezone');
if (timezone) {
now = utcToZonedTime(now, timezone);
}
const timeType = localStorage.getItem('timeType');
switch (timeType) {
switch (localStorage.getItem('timeType')) {
case 'percentageComplete':
this.setState({
time: (now.getHours() / 24).toFixed(2).replace('0.', '') + '%'
@@ -44,11 +47,7 @@ export default class Clock extends React.PureComponent {
const zero = localStorage.getItem('zero');
if (localStorage.getItem('seconds') === 'true') {
if (zero === 'false') {
sec = ':' + now.getSeconds();
} else {
sec = `:${('00' + now.getSeconds()).slice(-2)}`;
}
sec = `:${('00' + now.getSeconds()).slice(-2)}`;
}
if (localStorage.getItem('timeformat') === 'twentyfourhour') {
@@ -70,7 +69,7 @@ export default class Clock extends React.PureComponent {
}
if (zero === 'false') {
time = `${hours}:${now.getMinutes()}${sec}`;
time = `${hours}:${('00' + now.getMinutes()).slice(-2)}${sec}`;
} else {
time = `${('00' + hours).slice(-2)}:${('00' + now.getMinutes()).slice(-2)}${sec}`;
}
@@ -89,7 +88,7 @@ export default class Clock extends React.PureComponent {
componentDidMount() {
EventBus.on('refresh', (data) => {
if (data === 'clock') {
if (data === 'clock' || data === 'timezone') {
const element = document.querySelector('.clock-container');
if (localStorage.getItem('time') === 'false') {
@@ -111,6 +110,10 @@ export default class Clock extends React.PureComponent {
this.startTime(0);
}
componentWillUnmount() {
EventBus.remove('refresh');
}
render() {
let clockHTML = <h1 className='clock clock-container'>{this.state.time}<span className='ampm'>{this.state.ampm}</span></h1>;

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { utcToZonedTime } from 'date-fns-tz';
import EventBus from '../../../modules/helpers/eventbus';
import dtf from '../../../modules/helpers/date';
@@ -11,7 +12,7 @@ export default class DateWidget extends React.PureComponent {
super();
this.state = {
date: '',
weekNumber: ''
weekNumber: null
};
}
@@ -33,10 +34,18 @@ export default class DateWidget extends React.PureComponent {
}
getDate() {
const date = new Date();
let date = new Date();
const timezone = localStorage.getItem('timezone');
if (timezone) {
date = utcToZonedTime(date, timezone);
}
if (localStorage.getItem('weeknumber') === 'true') {
this.getWeekNumber(date);
} else if (this.state.weekNumber !== null) {
this.setState({
weekNumber: null
});
}
if (localStorage.getItem('dateType') === 'short') {
@@ -60,7 +69,8 @@ export default class DateWidget extends React.PureComponent {
year = dateDay;
break;
// DMY
default: break;
default:
break;
}
let format;
@@ -77,7 +87,8 @@ export default class DateWidget extends React.PureComponent {
case 'slashes':
format = `${day}/${month}/${year}`;
break;
default: break;
default:
break;
}
this.setState({
@@ -100,7 +111,7 @@ export default class DateWidget extends React.PureComponent {
componentDidMount() {
EventBus.on('refresh', (data) => {
if (data === 'date') {
if (data === 'date' || data === 'timezone') {
const element = document.querySelector('.date');
if (localStorage.getItem('date') === 'false') {
@@ -118,6 +129,10 @@ export default class DateWidget extends React.PureComponent {
this.getDate();
}
componentWillUnmount() {
EventBus.remove('refresh');
}
render() {
return <span className='date'>{this.state.date} <br/> {this.state.weekNumber}</span>;
}

View File

@@ -0,0 +1,350 @@
[
"Africa/Abidjan",
"Africa/Accra",
"Africa/Algiers",
"Africa/Bissau",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/El_Aaiun",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Khartoum",
"Africa/Lagos",
"Africa/Maputo",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Sao_Tome",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
"America/Argentina/Mendoza",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Asuncion",
"America/Atikokan",
"America/Bahia",
"America/Bahia_Banderas",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Cayenne",
"America/Chicago",
"America/Chihuahua",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Fort_Nelson",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Indianapolis",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Port_of_Spain",
"America/Porto_Velho",
"America/Puerto_Rico",
"America/Punta_Arenas",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Santarem",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Sitka",
"America/St_Johns",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Vancouver",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Macquarie",
"Antarctica/Mawson",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/Syowa",
"Antarctica/Troll",
"Antarctica/Vostok",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Atyrau",
"Asia/Baghdad",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Barnaul",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Chita",
"Asia/Choibalsan",
"Asia/Colombo",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Famagusta",
"Asia/Gaza",
"Asia/Hebron",
"Asia/Ho_Chi_Minh",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Irkutsk",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Kathmandu",
"Asia/Khandyga",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qostanay",
"Asia/Qyzylorda",
"Asia/Riyadh",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Srednekolymsk",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Tomsk",
"Asia/Ulaanbaatar",
"Asia/Urumqi",
"Asia/Ust-Nera",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yangon",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faroe",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/Stanley",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Sydney",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Astrakhan",
"Europe/Athens",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Helsinki",
"Europe/Istanbul",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Kirov",
"Europe/Lisbon",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Oslo",
"Europe/Paris",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/Saratov",
"Europe/Simferopol",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Ulyanovsk",
"Europe/Uzhgorod",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zaporozhye",
"Europe/Zurich",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Reunion",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Bougainville",
"Pacific/Chatham",
"Pacific/Chuuk",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Pohnpei",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Wake",
"Pacific/Wallis"
]

View File

@@ -35,6 +35,8 @@ export default class Weather extends React.PureComponent {
return null;
}
document.querySelector('.weather').style.fontSize = `${Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
let data = {
weather: [
{
@@ -60,7 +62,7 @@ export default class Weather extends React.PureComponent {
};
if (!this.state.weather.temp) {
data = await (await fetch (window.constants.WEATHER_URL + `/current?city=${this.state.location}&lang=${localStorage.getItem('language')}`)).json();
data = await (await fetch(window.constants.PROXY_URL + `/weather/current?city=${this.state.location}&lang=${localStorage.getItem('language')}`)).json();
}
if (data.cod === '404') {
@@ -88,7 +90,8 @@ export default class Weather extends React.PureComponent {
temp_text = '°F';
break;
// kelvin
default: break;
default:
break;
}
this.setState({
@@ -118,12 +121,8 @@ export default class Weather extends React.PureComponent {
EventBus.on('refresh', (data) => {
if (data === 'weather') {
this.getWeather();
document.querySelector('.weather').style.fontSize = `${Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
document.querySelector('.weather svg').style.fontSize = `${0.95 * Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
}
});
document.querySelector('.weather').style.fontSize = `${Number((localStorage.getItem('zoomWeather') || 100) / 100)}em`;
this.getWeather();
}
@@ -165,7 +164,7 @@ export default class Weather extends React.PureComponent {
<div className='weather'>
<WeatherIcon name={this.state.icon}/>
<span>{this.state.weather.temp + this.state.temp_text}</span>
{enabled('weatherdescription') ? <span className='loc' style={{ 'textTransform': 'capitalize' }}><br/>{this.state.weather.description}</span> : null}
{enabled('weatherdescription') ? <span className='loc'><br/>{this.state.weather.description}</span> : null}
<span className='minmax'>{minmax()}</span>
{enabled('humidity') ? <span className='loc'><br/><WiHumidity/>{this.state.weather.humidity}%</span> : null}
{enabled('windspeed') ? <span className='loc'><br/><WiWindy/>{this.state.weather.wind_speed}<span className='minmax'> m/s</span> {enabled('windDirection') ? <WindDirectionIcon degrees={this.state.weather.wind_degrees}/> : null}</span> : null}
@@ -177,4 +176,4 @@ export default class Weather extends React.PureComponent {
</div>
);
}
}
}

View File

@@ -2,10 +2,12 @@ import { WiDirectionDownLeft, WiDirectionDownRight, WiDirectionDown, WiDirection
export default function WindDirectionIcon(props) {
let icon;
// fix potential bug, idk what causes it but now it is fixed
let degrees = props.degrees;
// convert the number openweathermap gives us to closest direction or something
const directions = ['North', 'North-West', 'West', 'South-West', 'South', 'South-East', 'East', 'North-East'];
const direction = directions[Math.round(((props.degrees %= 360) < 0 ? props.degrees + 360 : props.degrees) / 45) % 8];
const direction = directions[Math.round(((degrees %= 360) < 0 ? degrees + 360 : degrees) / 45) % 8];
switch (direction) {
case 'North': icon = <WiDirectionUp/>; break;

View File

@@ -23,6 +23,7 @@
font-size: 0.7em;
margin: 0;
padding: 0;
text-transform: capitalize;
}
.minmax {

View File

@@ -8,7 +8,8 @@ import './scss/index.scss';
// the toast css is based on default so we need to import it
import 'react-toastify/dist/ReactToastify.min.css';
import '@fontsource/lexend-deca/400.css';
// this is opt-in btw, allows you to see your stats etc
import Stats from './modules/helpers/stats';
// language
import merge from '@material-ui/utils/esm/deepmerge';
@@ -22,11 +23,6 @@ if (languagecode === 'en') {
window.languagecode = 'en_GB';
}
// only load font if needed
if (languagecode === 'ru') {
require('@fontsource/montserrat/cyrillic-500.css');
}
// these are merged so if a string is untranslated it doesn't break mue
window.language = merge(require('./translations/en_GB.json'), require(`./translations/${window.languagecode}.json`));
@@ -36,6 +32,15 @@ if (window.languagecode !== 'en_GB' || window.languagecode !== 'en_US') {
}
window.constants = Constants;
// doesn't send to umami when offline mode is on
if (localStorage.getItem('stats') === 'true') {
window.stats = new Stats(window.constants.UMAMI_ID);
} else {
window.stats = {
tabLoad: () => '',
postEvent: () => ''
}
}
ReactDOM.render(
<App/>,

View File

@@ -1,14 +1,37 @@
// API URLs
export const API_URL = 'https://api.muetab.com';
export const UNSPLASH_URL = 'https://unsplash.muetab.com';
export const PEXELS_URL = 'https://pexels.muetab.com';
export const PROXY_URL = 'https://proxy.muetab.com';
export const MARKETPLACE_URL = 'https://marketplace.muetab.com';
export const WEATHER_URL = 'https://weather.muetab.com';
export const WEBSITE_URL = 'https://muetab.com';
export const SPONSORS_URL = 'https://sponsors.muetab.com';
export const GITHUB_URL = 'https://api.github.com';
export const BLOG_POST = 'https://blog.muetab.com/posts/version-5-1';
export const DDG_IMAGE_PROXY = 'https://external-content.duckduckgo.com/iu/?u=';
// Mue URLs
export const WEBSITE_URL = 'https://muetab.com';
export const PRIVACY_URL = 'https://muetab.com/privacy';
export const BLOG_POST = 'https://blog.muetab.com/posts/version-5-2';
export const FEEDBACK_FORM = 'https://api.formcake.com/api/form/349b56cb-7e2b-4004-b32b-e8964d217dd1/submission';
export const DDG_PROXY = 'https://external-content.duckduckgo.com/iu/?u=';
// Mue Info
export const ORG_NAME = 'mue';
export const REPO_NAME = 'mue';
export const EMAIL = 'hello@muetab.com';
export const TWITTER_HANDLE = 'getmue';
export const INSTAGRAM_HANDLE = 'mue.tab';
export const FACEBOOK_HANDLE = 'muetab';
export const DISCORD_SERVER = 'zv8C9F8';
export const COPYRIGHT_NAME = 'The Mue Authors';
export const COPYRIGHT_YEAR = '2018';
export const COPYRIGHT_LICENSE = 'BSD-3 License';
export const DONATE_USERNAME = 'davidjcralph'; // this only works if you use the same username for Patreon, GitHub and Ko-Fi
// umami
export const UMAMI_DOMAIN = 'https://umami.muetab.com';
export const UMAMI_ID = '1b97e723-199c-48d8-8992-17c4e22d4f3c';
// Offline images
export const OFFLINE_IMAGES = 20;
// Version
export const BETA_VERSION = false;
export const VERSION = '5.1.0';
export const VERSION = '5.2.0';

Some files were not shown because too many files have changed in this diff Show More