Co-authored-by: Alex Sparkes <turbomarshmello@gmail.com>
Co-authored-by: Wessel Tip <discord@go2it.eu>
Co-authored-by: Isaac Saunders <contact@eartharoid.me>
This commit is contained in:
David Ralph 2020-08-26 14:32:52 +01:00
parent 21ae1ff461
commit 91fefbf73c
54 changed files with 2044 additions and 513 deletions

2
.gitignore vendored
View File

@ -6,4 +6,4 @@ build/
# Files
package-lock.json
yarn-error.log
yarn.lock
yarn.lock

View File

@ -26,7 +26,8 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
* [Building](#building)
* [Credits](#credits)
* [Maintainers](#maintainers)
* [Contributors]()
* [Contributors](#contributors)
* [Translators](#translators)
* [Other](#other)
## Screenshot
@ -42,12 +43,10 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
* Search bar
* Settings - enable/disable various features and customise parts of Mue
* Update modal, copy button and more!
## Planned Features
* Multilingual support
* Marketplace - download custom photo packs, quote packs, preset settings and themes made by the community!
## Installation
*A demo of the tab can be found [here](https://mue.now.sh)*
*A demo of the tab can be found [here](https://demo.muetab.xyz)*
### Chrome
[![Chrome Web Store Logo](assets/chrome.png)](https://chrome.google.com/webstore/detail/mue/bngmbednanpcfochchhgbkookpiaiaid)
<br>
@ -67,15 +66,15 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
### Development
#### Requirements
<ol>
<li><a href='https://git-scm.com'>Git</a> (optional)</li>
<li><a href='https://git-scm.com'>Git</a></li>
<li><a href='https://nodejs.org'>Node.JS</a></li>
<li>A suitable browser</li>
</ol>
<h5>Starting</h5>
<ol>
<li> clone the repository using <code>git clone https://github.com/mue/mue.git</code>
<li> run <code>yarn</code> or <code>npm i</code> to install all needed dependencies
<li> run <code>yarn start</code> or <code>npm start</code> to start testing
<li> Clone the repository using <code>git clone https://github.com/mue/mue.git</code>
<li> Run <code>yarn</code> or <code>npm i</code> to install all needed dependencies
<li> Run <code>yarn start</code> or <code>npm start</code> to start testing
<li> Code your heart out! (See the sections below for how to build the extension)
</ol>
<h2>Building</h2>
@ -123,17 +122,22 @@ Mue is a fast, open and free-to-use browser extension that gives a new, fresh an
## Credits
### Maintainers
[ohlookitsderpy](https://github.com/ohlookitsderpy) - Founder, Lead development, Photographer <br>
[TurboMarshmello](https://github.com/TurboMarshmello) - Name, Lead design, Photographer <br>
[David Ralph (ohlookitsderpy)](https://github.com/ohlookitsderpy) - Founder, Lead development, Photographer <br>
[Alex Sparkes](https://github.com/alexsparkes) - Name, Lead design, Photographer <br>
### Contributors
[Wessel](https://github.com/Wessel) - Development <br>
[Isaac](https://github.com/eartharoid) - QA, Development, Photographer <br>
[Wessel Tip](https://github.com/Wessel) - Development <br>
[Isaac (Eartharoid)](https://github.com/eartharoid) - QA, Development, Photographer <br>
### Translators
English - [David Ralph (ohlookitsderpy)](https://github.com/ohlookitsderpy) & [Alex Sparkes](https://github.com/alexsparkes)
Dutch - [Wessel Tip](https://github.com/Wessel)
French - [Alex Sparkes](https://github.com/alexsparkes)
Norwegian - [Anders](https://github.com/FuryingFox)
### Other
[Pexels](https://pexels.com) - Stock photos used for offline mode
[Opera Forum](https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14) - Portions of code to add Opera support <br>
[Opera Forum](https://forums.opera.com/topic/25046/how-to-disable-completely-the-speed-dial/14) - Portions of code to add Opera support
[Google Fonts](https://fonts.google.com/specimen/Lexend+Deca) - Lexend Deca font
And many thanks to [Highholding](https://discord.bio/p/highholding), [Noa Shapira](#), [Roee Lupo](https://github.com/MrSheldon), [Jeroen](#), [Glasvegas](https://twitter.com/_glasvegas), [Anders](https://github.com/FuryingFox/), [Oded Shapira](https://twitter.com/dondishdev) and [Nikka Lai](#) for letting us use their wonderful photographs
And many thanks to [Highholding](https://discord.bio/p/highholding), [Noa Shapira](#), [Roee Lupo](https://github.com/RoeeLupo), [Jeroen](#), [Glasvegas](https://twitter.com/_glasvegas), [Anders](https://github.com/FuryingFox), [Oded Shapira](https://twitter.com/dondishdev), Jacob Tyrrell and [Nikka Lai](#) for letting us use their wonderful photographs

View File

@ -3,7 +3,7 @@
"offline_enabled": true,
"name": "Mue",
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
"version": "3.0.1",
"version": "4.0.0",
"browser_action": {
"default_icon": "./icons/extension-icon.png"
},

View File

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Mue",
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
"version": "3.0.1",
"version": "4.0.0",
"browser_action": {
"default_icon": "./icons/extension-icon.png"
},

View File

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Mue",
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
"version": "3.0.1",
"version": "4.0.0",
"browser_action": {
"default_icon": "./icons/extension-icon.png"
},

View File

@ -4,7 +4,7 @@
"author": "David \"ohlookitsderpy\" Ralph <d.ralph@muetab.xyz> (https://derpyenterprises.org)",
"maintainers": [
"David \"ohlookitsderpy\" Ralph <d.ralph@muetab.xyz> (https://derpyenterprises.org)",
"Alex \"TurboMarshmello\" Sparkes <a.sparkes@muetab.xyz> (https://github.com/TurboMarshmello)"
"Alex \"TurboMarshmello\" Sparkes <a.sparkes@muetab.xyz> (https://github.com/alexsparkes)"
],
"description": "Fast, open and free-to-use new tab page for most modern browsers.",
"repository": {
@ -13,21 +13,22 @@
"homepage": "https://muetab.xyz",
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
"license": "BSD-3-Clause",
"version": "3.0.1",
"version": "4.0.0",
"dependencies": {
"@eartharoid/dtf": "^1.0.7",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@muetab/quotes": "^1.0.0",
"copy-text-to-clipboard": "^2.2.0",
"detect-browser-language": "0.0.2",
"react": "^16.13.1",
"react-clock": "^2.4.0",
"react-dom": "^16.13.1",
"react-modal": "^3.11.2",
"react-scripts": "3.4.1",
"react-toastify": "^6.0.8",
"supports-webp": "^2.0.1"
},
"devDependencies": {
"react-scripts": "3.4.1",
"node-sass": "^4.14.1"
},
"scripts": {

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/></svg>

After

Width:  |  Height:  |  Size: 607 B

View File

@ -0,0 +1,10 @@
<svg width="20" height="20" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="200" cy="200" r="200" fill="url(#paint0_linear)"/>
<path d="M167 265.062L294.125 137.938L311 154.812L167 298.812L100.062 231.875L116.938 215L167 265.062Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="200" y1="0" x2="200" y2="400" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF5C25"/>
<stop offset="1" stop-color="#FF456E"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 490 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,17 +1,16 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' type='image/png' sizes='32x32' href='./icons/favicon-32x32.png'>
<link rel='icon' type='image/png' sizes='16x16' href='./icons/favicon-16x16.png'>
<title>New Tab</title>
</head>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' type='image/png' sizes='32x32' href='./icons/favicon-32x32.png'>
<link rel='icon' type='image/png' sizes='16x16' href='./icons/favicon-16x16.png'>
<title>New Tab</title>
</head>
<body>
<noscript>You need to enable JavaScript to use Mue.</noscript>
<div id='root'></div>
</body>
<body>
<noscript>You need to enable JavaScript to use Mue.</noscript>
<div id='root'></div>
</body>
</html>

View File

@ -1,20 +1,21 @@
import React from 'react';
import Background from './components/Background';
import Clock from './components/Clock';
import Greeting from './components/Greeting';
import Quote from './components/Quote';
import Search from './components/Search';
import Credit from './components/Credit';
import Background from './components/widgets/Background';
import Clock from './components/widgets/Clock';
import Greeting from './components/widgets/Greeting';
import Quote from './components/widgets/Quote';
import Search from './components/widgets/Search';
import Navbar from './components/Navbar';
import SettingsFunctions from './modules/settingsFunctions';
import { ToastContainer } from 'react-toastify';
import Modal from 'react-modal';
import RoomIcon from '@material-ui/icons/Room';
import './scss/index.scss';
import 'react-toastify/dist/ReactToastify.css';
const defaultSettings = require('./modules/defaultSettings.json');
const Settings = React.lazy(() => import('./components/Settings'));
const Update = React.lazy(() => import('./components/Update'));
// Modals are lazy loaded as a user won't use them every time they open a tab
const Settings = React.lazy(() => import('./components/modals/Settings'));
const Update = React.lazy(() => import('./components/modals/Update'));
const Marketplace = React.lazy(() => import('./components/modals/Marketplace'));
const Addons = React.lazy(() => import('./components/modals/Addons'));
const Welcome = React.lazy(() => import('./components/modals/Welcome'));
const renderLoader = () => <div></div>;
export default class App extends React.PureComponent {
@ -23,26 +24,17 @@ export default class App extends React.PureComponent {
this.state = {
settingsModal: false,
updateModal: false
updateModal: false,
marketplaceModal: false,
addonsModal: false,
quickAccessmodal: false,
welcomeModal: false
};
}
setDefaultSettings() {
localStorage.clear();
defaultSettings.forEach(element => localStorage.setItem(element.name, element.value));
// Set theme depending on user preferred
// if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) localStorage.setItem('darkTheme', true);
//else localStorage.setItem('darkTheme', false);
// Finally we set this to true so it doesn't run the function on every load
localStorage.setItem('firstRun', true);
window.location.reload();
}
// Render all the components
render() {
if (!localStorage.getItem('firstRun')) this.setDefaultSettings();
if (!localStorage.getItem('firstRun')) SettingsFunctions.setDefaultSettings();
let modalClassList = 'Modal';
if (localStorage.getItem('darkTheme') === 'true') modalClassList = 'Modal dark';
@ -52,26 +44,75 @@ export default class App extends React.PureComponent {
let language = require(`./translations/${localStorage.getItem('language')}.json`);
const theme = localStorage.getItem('theme');
if (theme) {
let style = document.createElement('link');
style.href = theme;
style.rel = 'stylesheet';
document.head.appendChild(style);
}
return (
<React.Fragment>
<div id='backgroundImage'></div>
<Background/>
<ToastContainer className='toast' position='bottom-right' autoClose={2500} hideProgressBar={false} newestOnTop={true} closeOnClick rtl={false} pauseOnFocusLoss />
<div id='center'>
<Search language={language.search} />
<Navbar settingsModalOpen={() => this.setState({ settingsModal: true })} updateModalOpen={() => this.setState({ updateModal: true })} />
<Greeting language={language.greeting} />
<Clock/>
<Quote/>
<Credit language={language.credit} />
<React.Suspense fallback={renderLoader()}>
<Modal id={'modal'} onRequestClose={() => this.setState({ settingsModal: false })} isOpen={this.state.settingsModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
<Settings language={language.settings} modalClose={() => this.setState({ settingsModal: false })} setDefaultSettings={() => this.setDefaultSettings()} />
</Modal>
<Modal onRequestClose={() => this.setState({ updateModal: false })} isOpen={this.state.updateModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
<Update language={language.update} modalClose={() => this.setState({ updateModal: false })} />
</Modal>
</React.Suspense>
<Search language={language.search} />
<Navbar settingsModalOpen={() => this.setState({ settingsModal: true })} updateModalOpen={() => this.setState({ updateModal: true })} />
<Greeting language={language.greeting} />
<Clock/>
<Quote language={language.toasts}/>
<div className='credits'>
<h1 id='photographer'>{language.credit}</h1>
<div id='backgroundCredits' className='tooltip'>
<RoomIcon className='locationicon'/>
<span className='tooltiptext' id='location'/>
</div>
</div>
<React.Suspense fallback={renderLoader()}>
<Modal id={'modal'} onRequestClose={() => this.setState({ settingsModal: false })} isOpen={this.state.settingsModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
<Settings
language={language.settings}
modalLanguage={language.modals}
modalClose={() => this.setState({ settingsModal: false })}
setDefaultSettings={() => SettingsFunctions.setDefaultSettings()}
openMarketplace={() => this.setState({ marketplaceModal: true, settingsModal: false })}
openAddons={() => this.setState({ settingsModal: false, addonsModal: true })}
toastLanguage={language.toasts}
/>
</Modal>
<Modal onRequestClose={() => this.setState({ updateModal: false })} isOpen={this.state.updateModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
<Update
language={language.update}
modalClose={() => this.setState({ updateModal: false })}
/>
</Modal>
<Modal onRequestClose={() => this.setState({ marketplaceModal: false })} isOpen={this.state.marketplaceModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
<Marketplace
language={language.marketplace}
modalLanguage={language.modals}
modalClose={() => this.setState({ marketplaceModal: false })}
openSettings={() => this.setState({ marketplaceModal: false, settingsModal: true })}
openAddons={() => this.setState({ marketplaceModal: false, addonsModal: true })}
toastLanguage={language.toasts}
/>
</Modal>
<Modal onRequestClose={() => this.setState({ addonsModal: false })} isOpen={this.state.addonsModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
<Addons
language={language.addons}
modalLanguage={language.modals}
modalClose={() => this.setState({ addonsModal: false })}
openSettings={() => this.setState({ addonsModal: false, settingsModal: true })}
openMarketplace={() => this.setState({ addonsModal: false, marketplaceModal: true })}
toastLanguage={language.toasts}
/>
</Modal>
<Modal onRequestClose={() => this.setState({ welcomeModal: false })} isOpen={this.state.welcomeModal} className={modalClassList} overlayClassName='Overlay' ariaHideApp={false}>
<Welcome
modalClose={() => this.setState({ welcomeModal: false })}
/>
</Modal>
</React.Suspense>
</div>
</React.Fragment>
);

View File

@ -1,18 +0,0 @@
/* eslint-disable jsx-a11y/heading-has-content */
import RoomIcon from '@material-ui/icons/Room';
import React from 'react';
export default class Credit extends React.PureComponent {
render() {
return (
<div className='credits'>
{/*<h1 id='location'></h1>*/}
<h1 id='photographer'>{this.props.language}</h1>
<div id='backgroundCredits' className='tooltip'>
<RoomIcon className='locationicon'/>
<span className='tooltiptext' id='location'/>
</div>
</div>
);
}
}

View File

@ -1,14 +1,18 @@
import React from 'react';
import RefreshIcon from '@material-ui/icons/Refresh';
import Gear from '@material-ui/icons/Settings';
import NewReleases from '@material-ui/icons/NewReleases';
import React from 'react';
export default class Navbar extends React.PureComponent {
render() {
let refreshHTML = <div className='navbar2'><RefreshIcon className='refreshicon' onClick={() => window.location.reload()} /></div>
let refreshHTML = <div className='navbar2' ><RefreshIcon className='refreshicon' onClick={() => window.location.reload()} /></div>
const refresh = localStorage.getItem('refresh');
if (refresh === 'false') refreshHTML = '';
const viewedUpdate = localStorage.getItem('viewedUpdate');
let update = <NewReleases className='refreshicon' onClick={this.props.updateModalOpen} />;
if (viewedUpdate === 'false') update = <NewReleases className='refreshicon' onClick={this.props.updateModalOpen} />
return (
<div className='navbar-container'>
<div className='navbar1'>
@ -16,7 +20,7 @@ export default class Navbar extends React.PureComponent {
</div>
{refreshHTML}
<div className={refresh === 'false' ? 'navbar2' : 'navbar3'}>
<NewReleases className='refreshicon' onClick={this.props.updateModalOpen} />
{update}
</div>
</div>
);

View File

@ -1,23 +0,0 @@
import React from 'react';
export default class Search extends React.PureComponent {
render() {
if (localStorage.getItem('searchBar') === 'false') return <div></div>;
let url;
switch (localStorage.getItem('searchEngine')) {
case 'duckduckgo': url = 'https://duckduckgo.com'; break;
case 'google': url = 'https://google.com/search'; break;
case 'bing': url = 'https://bing.com/search'; break;
default: url = 'https://duckduckgo.com'; break;
}
return <div id='searchBar' className='searchbar'>
<form id='searchBar' className='searchbarform' action={url}>
<input type='text' placeholder={this.props.language} name='q' id='searchtext' className='searchtext'/>
<div className='blursearcbBG'/>
</form>
</div>
}
}

View File

@ -0,0 +1,143 @@
import React from 'react';
import LocalMallIcon from '@material-ui/icons/LocalMall';
import { toast } from 'react-toastify';
import dtf from '@eartharoid/dtf';
import Item from './marketplace/Item';
import MarketplaceFunctions from '../../modules/marketplaceFunctions';
export default class Addons extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
installed: [],
item_data: {
name: 'Name',
author: 'Author',
description: 'Description',
updated: '???',
version: '1.0.0',
icon: ''
}
}
}
async toggle(type, type2, data) {
if (type === 'item') {
let installed = JSON.parse(localStorage.getItem('installed'));
let info = installed.find(i => i.name === data).content;
this.setState({
current_data: { type: type2, name: data, content: info },
item_data: {
name: info.data.name,
author: info.data.author,
description: MarketplaceFunctions.urlParser(info.data.description.replace(/\n/g, '<br>')),
updated: dtf('n_D MMM YYYY', info.updated, 'en-GB'),
version: info.data.version,
icon: info.data.screenshot_url
}
});
document.getElementById('marketplace').style.display = 'none';
document.getElementById('item').style.display = 'block';
} else {
document.getElementById('marketplace').style.display = 'block';
document.getElementById('item').style.display = 'none';
}
}
uninstall() {
let installed = JSON.parse(localStorage.getItem('installed'));
const uninstallStuff = () => {
for (let i = 0; i < installed.length; i++) {
if (installed[i].name === this.state.current_data.name) {
installed.splice(i, 1);
break;
}
}
localStorage.setItem('installed', JSON.stringify(installed));
toast(this.props.toastLanguage.removed);
this.toggle();
this.componentDidMount();
}
switch (this.state.current_data.type) {
case 'settings':
let oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
localStorage.clear();
oldSettings.forEach(item => localStorage.setItem(item.name, item.value));
uninstallStuff();
break;
default:
try {
localStorage.removeItem(this.state.current_data.type);
uninstallStuff();
} catch (e) {
console.log('invalid');
}
}
}
componentDidMount() {
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
document.getElementById('center').classList.toggle('backgroundEffects');
this.setState({ installed: JSON.parse(localStorage.getItem('installed')) });
}
componentWillUnmount() {
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
document.getElementById('center').classList.toggle('backgroundEffects');
}
render() {
if (this.state.installed.length === 0) {
return <div className='content'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.modalLanguage.title}</h1>
<div className="tab">
<button className="tablinks" onClick={this.props.openMarketplace}>{this.props.modalLanguage.marketplace}</button>
<button className="tablinks" id="active">{this.props.modalLanguage.addons}</button>
<button className="tablinks" onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
</div>
<div id='marketplace'>
{ /*<input id='file-input' type='file' name='name' className='hidden' />
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>Sideload</button> */}
<h1>{this.props.language.added}</h1>
<div className="items">
<div className="emptyMessage">
<LocalMallIcon />
<h1>{this.props.language.empty.title}</h1>
<p className="description">{this.props.language.empty.description}</p>
<button className="goToMarket" onClick={this.props.openMarketplace}>{this.props.language.empty.button}</button>
</div>
</div>
</div>
</div>
}
return <div className='content'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.modalLanguage.title}</h1>
<div className="tab">
<button className="tablinks" onClick={this.props.openMarketplace}>{this.props.modalLanguage.marketplace}</button>
<button className="tablinks" id="active">{this.props.modalLanguage.addons}</button>
<button className="tablinks" onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
</div>
<div id='marketplace'>
{ /*<input id='file-input' type='file' name='name' className='hidden' />
<button className='addToMue sideload' onClick={() => document.getElementById('file-input').click()}>Sideload</button> */}
<h1>{this.props.language.added}</h1>
<div className="items">
{this.state.installed.map((item) =>
<div className="item" onClick={()=> this.toggle('item', item.type, item.name)}>
<img alt="icon" src={item.content.data.icon_url} />
<div className="details">
<h4>{item.content.data.name}</h4>
<p>{item.content.data.author}</p>
</div>
</div>)}
</div>
</div>
<Item button={<button className="removeFromMue" onClick={() => this.uninstall()}>{this.props.language.product.buttons.remove}</button>} data={this.state.item_data} function={()=> this.toggle()} language={this.props.language.product} />
</div>;
}
}

View File

@ -0,0 +1,265 @@
import React from 'react';
import WifiOffIcon from '@material-ui/icons/WifiOff';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import { toast } from 'react-toastify';
import dtf from '@eartharoid/dtf';
import Item from './marketplace/Item';
import MarketplaceFunctions from '../../modules/marketplaceFunctions';
import * as Constants from '../../modules/constants';
import Items from './marketplace/Items';
export default class Marketplace extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
themes: [],
settings: [],
photo_packs: [],
quote_packs: [],
see_more: [],
see_more_type: '',
current_data: {
type: '',
name: '',
content: {}
},
button: '',
featured: {},
done: false,
item_data: {
name: 'Name',
author: 'Author',
description: 'Description',
updated: '???',
version: '1.0.0',
icon: ''
}
}
this.offlineHTML = <div className='content'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.modalLanguage.title}</h1>
<div className="tab">
<button className="tablinks" id="active">{this.props.modalLanguage.marketplace}</button>
<button className="tablinks" onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
<button className="tablinks"
onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
</div>
<div id='marketplace'>
<div className="emptyMessage" style={{"marginTop": "20px", "transform": "translateY(80%)"}}>
<WifiOffIcon />
<h1>{this.props.language.offline.title}</h1>
<p className="description">{this.props.language.offline.description}</p>
</div>
</div>
</div>;
}
async toggle(type, type2, data) {
if (type === 'seemore') {
document.getElementById('marketplace').style.display = 'none';
document.getElementById('seemore').style.display = 'block';
return this.setState({
see_more: this.state[type2],
see_more_type: type2
});
}
if (type === 'item') {
let info;
try {
info = await fetch(`${Constants.MARKETPLACE_URL}/item/${type2}/${data}`);
info = await info.json();
} catch (e) {
return toast(this.props.toastLanguage.error);
}
this.setState({
current_data: { type: type2, name: data, content: info },
item_data: {
name: info.data.name,
author: info.data.author,
description: MarketplaceFunctions.urlParser(info.data.description.replace(/\n/g, '<br>')),
updated: dtf('n_D MMM YYYY', info.updated, 'en-GB'),
version: info.data.version,
icon: info.data.screenshot_url
}
});
document.getElementById('marketplace').style.display = 'none';
document.getElementById('seemore').style.display = 'none';
document.getElementById('item').style.display = 'block';
let button = <button className="addToMue" onClick={() => this.install()}>{this.props.language.product.buttons.addtomue}</button>;
let installed = JSON.parse(localStorage.getItem('installed'));
if (installed.some(item => item.name === data)) button = <button className="removeFromMue" onClick={() => this.uninstall()}>{this.props.language.product.buttons.remove}</button>;
this.setState({ button: button });
} else {
document.getElementById('marketplace').style.display = 'block';
document.getElementById('item').style.display = 'none';
document.getElementById('seemore').style.display = 'none';
}
}
async getItems() {
let data = await fetch(Constants.MARKETPLACE_URL + '/all');
data = await data.json();
let data2 = await fetch(Constants.MARKETPLACE_URL + '/featured');
data2 = await data2.json();
this.setState({
themes: data.data.themes,
settings: data.data.settings,
photo_packs: data.data.photo_packs,
quote_packs: data.data.quote_packs,
see_more: data.data.photo_packs,
featured: data2.data,
done: true
});
}
async install() {
let installed = JSON.parse(localStorage.getItem('installed'));
let button;
const installStuff = () => {
installed.push(this.state.current_data);
localStorage.setItem('installed', JSON.stringify(installed));
toast(this.props.toastLanguage.installed);
button = <button className="removeFromMue" onClick={() => this.uninstall()}>{this.props.language.product.buttons.remove}</button>;
this.setState({ button: button });
}
switch (this.state.current_data.type) {
case 'settings':
localStorage.removeItem('backup_settings');
let oldSettings = [];
for (const key of Object.keys(localStorage)) oldSettings.push({name: key, value: localStorage.getItem(key)});
localStorage.setItem('backup_settings', JSON.stringify(oldSettings));
this.state.current_data.content.data.settings.forEach(element => localStorage.setItem(element.name, element.value));
installStuff();
break;
case 'photo_packs':
localStorage.setItem('photo_packs', JSON.stringify(this.state.current_data.content.data.photos));
installStuff();
break;
case 'theme':
localStorage.setItem('theme', this.state.current_data.content.data.theme);
installStuff();
break;
case 'quote_packs':
localStorage.setItem('quote_packs', JSON.stringify(this.state.current_data.content.data.quotes));
installStuff();
break;
default:
console.log('invalid');
}
}
uninstall() {
let installed = JSON.parse(localStorage.getItem('installed'));
let button;
const uninstallStuff = () => {
for (let i = 0; i < installed.length; i++) {
if (installed[i].name === this.state.current_data.name) {
installed.splice(i, 1);
break;
}
}
localStorage.setItem('installed', JSON.stringify(installed));
toast(this.props.toastLanguage.removed);
button = <button className="addToMue" onClick={() => this.install()}>{this.props.language.product.buttons.addtomue}</button>;
this.setState({ button: button });
}
switch (this.state.current_data.type) {
case 'settings':
let oldSettings = JSON.parse(localStorage.getItem('backup_settings'));
localStorage.clear();
oldSettings.forEach(item => localStorage.setItem(item.name, item.value));
uninstallStuff();
break;
default:
try {
localStorage.removeItem(this.state.current_data.type);
uninstallStuff();
} catch (e) {
console.log('invalid');
}
}
}
componentDidMount() {
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
document.getElementById('center').classList.toggle('backgroundEffects');
if (navigator.onLine === false) return;
this.getItems();
}
componentWillUnmount() {
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
document.getElementById('center').classList.toggle('backgroundEffects');
}
render() {
if (navigator.onLine === false || this.state.done === false) {
return this.offlineHTML;
}
return <div className='content'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.modalLanguage.title}</h1>
<div className="tab">
<button className="tablinks" id="active">{this.props.modalLanguage.marketplace}</button>
<button className="tablinks" onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
<button className="tablinks"
onClick={this.props.openSettings}>{this.props.modalLanguage.settings}</button>
</div>
<div id='marketplace'>
<div className="featured" style={{backgroundColor: this.state.featured.colour}}>
<p>{this.state.featured.title}</p>
<h1>{this.state.featured.name}</h1>
<button className="addToMue" onClick={() => window.location.href =
this.state.featured.buttonLink}>{this.state.featured.buttonText}</button>
</div>
<Items
title={this.props.language.photo_packs}
seeMoreTitle={this.props.language.see_more}
items={this.state.photo_packs.slice(0, 3)}
toggleFunction={(input) => this.toggle('item', 'photo_packs', input)}
seeMore={true}
seeMoreFunction={() => this.toggle('seemore', 'photo_packs')} />
<Items
title={this.props.language.preset_settings}
seeMoreTitle={this.props.language.see_more}
items={this.state.settings.slice(0, 3)}
toggleFunction={(input) => this.toggle('item', 'settings', input)}
seeMore={true}
seeMoreFunction={() => this.toggle('seemore', 'settings')} />
<Items
title={this.props.language.quote_packs}
seeMoreTitle={this.props.language.see_more}
items={this.state.quote_packs.slice(0, 3)}
toggleFunction={(input) => this.toggle('item', 'quote_packs', input)}
seeMore={true}
seeMoreFunction={() => this.toggle('seemore', 'quote_packs')} />
<Items
title={this.props.language.themes}
seeMoreTitle={this.props.language.see_more}
items={this.state.themes.slice(0, 3)}
toggleFunction={(input) => this.toggle('item', 'theme', input)}
seeMore={true}
seeMoreFunction={() => this.toggle('seemore', 'themes')} />
</div>
<Item button={this.state.button} data={this.state.item_data} function={()=> this.toggle()} language={this.props.language.product} />
<div id='seemore'>
<ArrowBackIcon className='backArrow' onClick={() => this.toggle()} />
<Items
title={this.props.language.see_more}
seeMoreTitle={this.props.language.see_more}
toggleFunction={(input) => this.toggle('item', this.state.see_more_type, input)}
items={this.state.see_more}
/>
</div>
</div>;
}
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import ExpandMore from '@material-ui/icons/ExpandMore';
import * as SettingsFunctions from '../modules/settingsFunctions';
import SettingsFunctions from '../../modules/settingsFunctions';
import Checkbox from './settings/Checkbox';
import Slider from './settings/Slider';
import { toast } from 'react-toastify';
@ -22,7 +22,7 @@ export default class Settings extends React.PureComponent {
case 'customSearchEngine': document.getElementById('searchEngine').value = 'DuckDuckGo'; break;
default: console.log('[ERROR] resetItem requires a key!');
}
toast('Reset successfully!');
toast(this.props.toastLanguage.reset);
}
updateCurrent() {
@ -108,16 +108,9 @@ export default class Settings extends React.PureComponent {
document.getElementById('customBackgroundHex').innerText = hex;
}*/
document.getElementById('backgroundImage').classList.toggle('backgroundEffects');
document.getElementById('center').classList.toggle('backgroundEffects');
/* document.addEventListener('keyup', (event) => {
if (event.keyCode === 13) this.saveStuff();
if (event.keyCode === 27) {
document.getElementById('root').classList.toggle('backgroundEffects');
this.props.modalClose();
}
});*/
}
componentWillUnmount() {
@ -130,12 +123,17 @@ export default class Settings extends React.PureComponent {
//if (localStorage.getItem('animations') === 'true') expandClassList = 'all 0.5 ease 0s';
return <div className='content'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.language.title}</h1>
<p>{this.props.language.description}</p>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1>{this.props.modalLanguage.title}</h1>
<div className="tab">
<button className="tablinks" onClick={this.props.openMarketplace}>{this.props.modalLanguage.marketplace}</button>
<button className="tablinks" onClick={this.props.openAddons}>{this.props.modalLanguage.addons}</button>
<button className="tablinks" id="active">{this.props.modalLanguage.settings}</button>
</div>
<br />
<div className='columns'>
<div className='section'>
<h4>{this.props.language.time.title}</h4>
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[0], document.getElementsByClassName('expandIcons')[0])}>{this.props.language.time.title}</h4>
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[0], document.getElementsByClassName('expandIcons')[0])} />
<Slider name='time' />
<li className='extraSettings'>
@ -147,7 +145,7 @@ export default class Settings extends React.PureComponent {
</li>
</div>
<div className='section'>
<h4>{this.props.language.greeting.title}</h4>
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[1], document.getElementsByClassName('expandIcons')[1])}>{this.props.language.greeting.title}</h4>
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[1], document.getElementsByClassName('expandIcons')[1])} />
<Slider name='greeting' />
<li className='extraSettings'>
@ -160,7 +158,7 @@ export default class Settings extends React.PureComponent {
</li>
</div>
<div className='section'>
<h4>{this.props.language.quote.title}</h4>
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[2], document.getElementsByClassName('expandIcons')[2])}>{this.props.language.quote.title}</h4>
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[2], document.getElementsByClassName('expandIcons')[2])} />
<Slider name='quote' />
<li className='extraSettings'>
@ -168,7 +166,7 @@ export default class Settings extends React.PureComponent {
</li>
</div>
<div className='section'>
<h4>{this.props.language.background.title}</h4>
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[3], document.getElementsByClassName('expandIcons')[3])}>{this.props.language.background.title}</h4>
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[3], document.getElementsByClassName('expandIcons')[3])} />
<Slider name='background' override='customBackground' />
<li className='extraSettings'>
@ -203,7 +201,7 @@ export default class Settings extends React.PureComponent {
</li>
</div>
<div className='section'>
<h4>{this.props.language.searchbar.title}</h4>
<h4 onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[4], document.getElementsByClassName('expandIcons')[4])}>{this.props.language.searchbar.title}</h4>
<ExpandMore style={{ 'transition': 'all 0.5s ease 0s' }} className='expandIcons' onClick={() => SettingsFunctions.toggleExtra(document.getElementsByClassName('extraSettings')[4], document.getElementsByClassName('expandIcons')[4])} />
<Slider name='searchBar' />
<li className='extraSettings'>
@ -213,11 +211,17 @@ export default class Settings extends React.PureComponent {
<option value='duckduckgo'>DuckDuckGo</option>
<option value='google'>Google</option>
<option value='bing'>Bing</option>
{ /* <option value='custom'>Custom</option> */ }
<option value='yahoo'>Yahoo</option>
<option value='ecosia'>Ecosia</option>
<option value='yandex'>Yandex</option>
<option value='qwant'>Qwant</option>
<option value='ask'>Ask</option>
<option value='startpage'>Startpage</option>
{/* <option value='custom'>Custom</option> */}
</select>
</ul>
<ul id='searchEngineInput' style={{ display: 'none' }}>
<p>Custom Search URL <span className='modalLink' onClick={() => this.resetItem('customSearchEngine')}>Reset</span></p>
<p style={{"marginTop": "0px"}}>Custom Search URL <span className='modalLink' onClick={() => this.resetItem('customSearchEngine')}>Reset</span></p>
<input type='text' id='customSearchEngine'></input>
</ul>
</li>
@ -243,8 +247,10 @@ export default class Settings extends React.PureComponent {
<h4 htmlFor='9'>{this.props.language.language} </h4>
<select className='select-css' name='9' id='language' onChange={() => localStorage.setItem('language', document.getElementById('language').value)}>
<option className='choices' value='en'>English</option>
<option className='choices' value='nl'>Dutch</option>
<option className='choices' value='fr'>French</option>
<option className='choices' value='nl'>Nederlands</option>
<option className='choices' value='fr'>Français</option>
<option className='choices' value='no'>Norsk</option>
<option className='choices' value='ru'>Russian</option>
</select>
</div>
<button className='apply' onClick={() => SettingsFunctions.saveStuff()}>{this.props.language.apply}</button>

View File

@ -1,5 +1,5 @@
import React from 'react';
import * as Constants from '../modules/constants';
import * as Constants from '../../modules/constants';
export default class Update extends React.PureComponent {
constructor(...args) {
@ -33,15 +33,18 @@ export default class Update extends React.PureComponent {
}
componentDidMount() {
localStorage.setItem('viewedUpdate', true);
this.getUpdate();
}
render() {
return <div className='content'>
return <div className='updateContent'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<h1 style={{ 'marginBottom':'-10px' }} dangerouslySetInnerHTML={{__html: this.state.title}}></h1>
<h5 style={{ 'lineHeight':'0px' }}> By Mue <span dangerouslySetInnerHTML={{__html: this.state.date}}></span></h5>
<p style={{ 'padding': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{__html: this.state.content}}></p>
<img alt='update picture' src='https://i.redd.it/dtm7e7ggxxh51.png' />
<figcaption>Image of JIF.</figcaption>
<p dangerouslySetInnerHTML={{__html: this.state.content}}></p>
</div>;
}
}

View File

@ -0,0 +1,31 @@
import React from 'react';
import EmailIcon from '@material-ui/icons/Email';
export default class Welcome extends React.PureComponent {
render() {
return <div className='welcomeContent'>
<span className='closeModal' onClick={this.props.modalClose}>&times;</span>
<div className="welcomeModalText">
<h2 className="subtitle">Welcome to</h2>
<h1 className="welcometitle">Mue Tab</h1>
<img alt="celebration" style={{"height": "200px", "width": "auto"}} src="./././icons/undraw_celebration.svg" />
<h2 className="subtitle">Information</h2>
<p>Thank you for downloading Mue Tab,<br/> we hope you enjoy your time with our extension.</p>
<h2 className="subtitle">Tutorials</h2>
<a href=''>General Start</a>
<br/>
<a href='https://blog.muetab.xyz/welcome-to-marketplace/'>Marketplace</a>
<br/>
<a href=''>Submitting Photos</a>
<br/>
<a href=''>Settings</a>
<h2 className="subtitle">Support</h2>
<img alt="twitter" href="https://twitter.com/getmue" className="icon" src="./././icons/iconmonstr-twitter.svg"/>
<img alt="discord" href="https://discord.gg/kJsufA9" className="icon" src="https://cdn.discordapp.com/attachments/252071498397777921/747860127171739758/Discord-Logo-Black.svg"/>
<EmailIcon />
<br/>
<button className="close" onClick={this.props.modalClose}>Close</button>
</div>
</div>;
}
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
export default class Item extends React.PureComponent {
render() {
return (
<div id='item'>
<br/>
<ArrowBackIcon className='backArrow' onClick={this.props.function} />
<br/>
<h1>{this.props.data.name}</h1>
{this.props.button}
<br/><br/>
<img alt="product" src={'https://external-content.duckduckgo.com/iu/?u=' + this.props.data.icon} />
<div className="informationContainer">
<div className="productInformation">
<h4>{this.props.language.information}</h4>
<ul>
<br/>
<li className="header">{this.props.language.last_updated}</li>
<li>{this.props.data.updated}</li>
<br/>
<li className="header">{this.props.language.version}</li>
<li>{this.props.data.version}</li>
<br/>
<li className="header">{this.props.language.author}</li>
<li>{this.props.data.author}</li>
</ul>
</div>
<div className="productInformation">
<ul>
<li className="header">{this.props.language.notice.title}</li>
<li id='updated'>{this.props.language.notice.description}</li>
</ul>
</div>
</div>
<br/>
<h1>{this.props.language.overview}</h1>
<p className="description" dangerouslySetInnerHTML={{__html: this.props.data.description}}></p>
</div>
);
}
}

View File

@ -0,0 +1,25 @@
import React from 'react';
export default class Items extends React.PureComponent {
render() {
let seeMoreHTML;
if (this.props.seeMore === true) seeMoreHTML = <button className='addToMue seemore' onClick={this.props.seeMoreFunction}>{this.props.seeMoreTitle}</button>;
return (
<div>
{seeMoreHTML}
<h1>{this.props.title}</h1>
<div className="items">
{this.props.items.map((item) =>
<div className="item" onClick={() => this.props.toggleFunction(item.name)}>
<img alt="icon" src={'https://external-content.duckduckgo.com/iu/?u=' + item.icon_url} />
<div className="details">
<h4>{item.display_name}</h4>
<p>{item.author}</p>
</div>
</div>)}
</div>
</div>
);
}
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import SettingsFunctions from '../../../modules/settingsFunctions';
import CheckboxUI from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
export default class Checkbox extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
checked: true
}
}
render() {
const handleChange = () => {
SettingsFunctions.setItem(this.props.name);
let checked;
if (this.state.checked === true) checked = false;
else checked = true;
this.setState({ checked: checked });
}
let value = localStorage.getItem(this.props.name);
switch (value) {
case 'true': value = true; break;
case 'false': value = false; break;
default: value = false;
}
this.setState({ checked: value });
return (
<React.Fragment>
<FormControlLabel
control={<CheckboxUI name="checkedB" color="primary" checked={this.state.checked} onChange={handleChange} />}
label={this.props.text}
/>
<br/>
</React.Fragment>
);
}
}

View File

@ -1,5 +1,5 @@
import React from 'react';
import * as SettingsFunctions from '../../modules/settingsFunctions';
import SettingsFunctions from '../../../modules/settingsFunctions';
export default class Slider extends React.PureComponent {
render() {

View File

@ -1,13 +0,0 @@
import React from 'react';
import * as SettingsFunctions from '../../modules/settingsFunctions';
export default class Checkbox extends React.PureComponent {
render() {
return (
<ul>
<input name={this.props.name} type="checkbox" onClick={() => SettingsFunctions.setItem(this.props.name)} id={this.props.name + 'Status'} />
<label htmlFor={this.props.name}>{this.props.text}</label>
</ul>
);
}
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import supportsWebP from 'supports-webp';
import * as Constants from '../modules/constants';
import * as Constants from '../../modules/constants';
export default class Background extends React.PureComponent {
doOffline() {
@ -24,7 +24,15 @@ export default class Background extends React.PureComponent {
}
async setBackground() {
if (localStorage.getItem('offlineMode')=== 'true') return this.doOffline();
if (localStorage.getItem('offlineMode') === 'true') return this.doOffline();
const photoPack = JSON.parse(localStorage.getItem('photo_packs'));
if (photoPack) {
let background = photoPack[Math.floor(Math.random() * photoPack.length)];
document.getElementById('backgroundCredits').style.display = 'none'; // Hide the location icon
document.getElementById('photographer').style.display = 'none';
return document.getElementById('backgroundImage').setAttribute('style', `-webkit-filter:blur(${localStorage.getItem('blur')}px); background-image: url(${background.url.default})`); // Set background and blur etc
}
const colour = localStorage.getItem('customBackgroundColour');
if (colour) {
@ -54,7 +62,7 @@ export default class Background extends React.PureComponent {
requestURL = 'https://unsplash.muetab.xyz/getImage';
break;
default:
if (await supportsWebP && enabled === 'true') requestURL = Constants.API_URL +'/getImage?webp=true';
if (await supportsWebP && enabled === 'true') requestURL = Constants.API_URL +'/getImage?webp=true';
else requestURL = Constants.API_URL + '/getImage?category=Outdoors';
break;
}
@ -79,6 +87,6 @@ export default class Background extends React.PureComponent {
}
render() {
return null; // React gets annoyed if I don't put anything here or use "return;"
return <div id='backgroundImage'></div>;
}
}

View File

@ -69,7 +69,7 @@ export default class Clock extends React.PureComponent {
render() {
let clockHTML = <h1 className='clock'>{this.state.time}<span className='ampm'>{this.state.ampm}</span> </h1>;
if (localStorage.getItem('analog') === 'true') clockHTML = <Analog className='analogclock' value={this.state.time}/>;
if (localStorage.getItem('analog') === 'true') clockHTML = <Analog className='analogclock' value={this.state.time} renderHourMarks={false} renderMinuteMarks={false} />;
return clockHTML;
}
}

View File

@ -1,9 +1,8 @@
import React from 'react';
import Quotes from '@muetab/quotes';
import copy from 'copy-text-to-clipboard';
import FileCopy from '@material-ui/icons/FilterNone';
import { toast } from 'react-toastify';
import * as Constants from '../modules/constants';
import * as Constants from '../../modules/constants';
export default class Quote extends React.PureComponent {
constructor(...args) {
@ -23,10 +22,19 @@ export default class Quote extends React.PureComponent {
}
async getQuote() {
const quotePack = JSON.parse(localStorage.getItem('quote_packs'));
if (quotePack) {
const data = quotePack[Math.floor(Math.random() * quotePack.length)]
return this.setState({
quote: '"' + data.quote + '"',
author: data.author
});
}
if (localStorage.getItem('offlineMode') === 'true') return this.doOffline();
try { // First we try and get a quote from the API...
let data = await fetch(Constants.API_URL +'/getQuote');
let data = await fetch(Constants.API_URL + '/getQuote');
data = await data.json();
if (data.statusCode === 429) this.doOffline(); // If we hit the ratelimit, we fallback to local quotes
this.setState({
@ -39,8 +47,8 @@ export default class Quote extends React.PureComponent {
}
copyQuote() {
copy(`${this.state.quote} - ${this.state.author}`);
toast('Quote copied!');
navigator.clipboard.writeText(`${this.state.quote} - ${this.state.author}`);
toast(this.props.language.quote);
}
componentDidMount() {

View File

@ -0,0 +1,38 @@
import React from 'react';
import SearchIcon from '@material-ui/icons/Search';
export default class Search extends React.PureComponent {
render() {
if (localStorage.getItem('searchBar') === 'false') return <div></div>;
let url;
let query = 'q';
switch (localStorage.getItem('searchEngine')) {
case 'duckduckgo': url = 'https://duckduckgo.com'; break;
case 'google': url = 'https://google.com/search'; break;
case 'bing': url = 'https://bing.com/search'; break;
case 'yahoo': url ='https://search.yahoo.com/search'; break;
case 'ecosia': url = 'https://ecosia.org/search'; break;
case 'yandex': url = 'https://yandex.ru/search'; query = 'text'; break;
case 'qwant': url = 'https://www.qwant.com/'; break;
case 'ask': url = 'https://ask.com/web'; break;
case 'startpage': url = 'https://www.startpage.com/sp/search'; break;
default: url = 'https://duckduckgo.com'; break;
}
const searchButton = () => {
let value = document.getElementById('searchtext').value;
if (!value) value = 'mue fast';
window.location.href = url + '?q=' + value;
}
return <div id='searchBar' className='searchbar'>
<form id='searchBar' className='searchbarform' action={url}>
<SearchIcon onClick={() => searchButton()} />
<input type='text' placeholder={this.props.language} name={query} id='searchtext' className='searchtext'/>
<div className='blursearcbBG'/>
</form>
</div>
}
}

View File

@ -2,6 +2,9 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './scss/index.scss';
import 'react-toastify/dist/ReactToastify.css';
ReactDOM.render(
<App/>,
document.getElementById('root')

View File

@ -1,2 +1,4 @@
export const API_URL = 'https://api.muetab.xyz';
export const UNSPLASH_URL = 'https://unsplash.muetab.xyz';
export const MARKETPLACE_URL = 'https://marketplace.muetab.xyz';
export const OFFLINE_IMAGES = 20;

View File

@ -43,10 +43,6 @@
"name": "defaultGreetingMessage",
"value": true
},
{
"name": "language",
"value": "en"
},
{
"name": "backgroundAPI",
"value": "mue"
@ -58,5 +54,13 @@
{
"name": "copyButton",
"value": false
},
{
"name": "installed",
"value": "[]"
},
{
"name": "searchEngine",
"value": "duckduckgo"
}
]

View File

@ -0,0 +1,6 @@
export default class MarketplaceFunctions {
static urlParser (input) { // based on https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links
let urlPattern = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_+.~#?&//=]*)/;
return input.replace(urlPattern, '<a href="$&" target="_blank">$&</a>');
}
}

View File

@ -1,4 +1,6 @@
function saveFile(data, filename = 'file') {
import detectBrowserLanguage from 'detect-browser-language';
const saveFile = (data, filename = 'file') => {
if (!data) return console.error('No data');
if (typeof data === 'object') data = JSON.stringify(data, undefined, 4);
@ -14,47 +16,78 @@ function saveFile(data, filename = 'file') {
a.dispatchEvent(e);
}
export function exportSettings() {
let settings = {};
for (const key of Object.keys(localStorage)) settings[key] = localStorage.getItem(key);
saveFile(settings, 'mue-settings.json');
}
const defaultSettings = require('./defaultSettings.json');
export function setItem(key, value) {
let old = localStorage.getItem(key);
let val = true;
if (old !== null && !value) {
if (old === 'true') val = false;
if (old === 'false') val = true;
export default class SettingsFunctions {
static exportSettings() {
let settings = {};
for (const key of Object.keys(localStorage)) settings[key] = localStorage.getItem(key);
saveFile(settings, 'mue-settings.json');
}
localStorage.setItem(key, val);
}
static setItem(key, value) {
let old = localStorage.getItem(key);
let val = true;
export function toggleExtra(element, element2) {
(element.style.display === 'none' || !element.style.display) ? element.style.display = 'block' : element.style.display = 'none';
(element2.style.transform === 'rotate(-180deg)') ? element2.style.transform = 'rotate(0)' : element2.style.transform = 'rotate(-180deg)';
}
if (old !== null && !value) {
if (old === 'true') val = false;
if (old === 'false') val = true;
}
export function setSearchEngine(input) {
const searchEngineInput = document.getElementById('searchEngineInput');
if (input === 'custom') {
searchEngineInput.enabled = 'false';
searchEngineInput.style.display = 'block';
} else {
searchEngineInput.style.display = 'none';
searchEngineInput.enabled = 'true';
localStorage.setItem('searchEngine', input);
localStorage.setItem(key, val);
}
}
export function saveStuff() {
localStorage.setItem('blur', document.getElementById('blurRange').value); // this is better than inline onChange for performance
localStorage.setItem('greetingName', document.getElementById('greetingName').value);
localStorage.setItem('customBackground', document.getElementById('customBackground').value);
if (!document.getElementById('searchEngineInput').enabled === 'false') {
localStorage.setItem('customSearchEngine', document.getElementById('searchEngineInput').value);
static toggleExtra(element, element2) {
(element.style.display === 'none' || !element.style.display) ? element.style.display = 'block' : element.style.display = 'none';
(element2.style.transform === 'rotate(-180deg)') ? element2.style.transform = 'rotate(0)' : element2.style.transform = 'rotate(-180deg)';
}
static setSearchEngine(input) {
const searchEngineInput = document.getElementById('searchEngineInput');
if (input === 'custom') {
searchEngineInput.enabled = 'false';
searchEngineInput.style.display = 'block';
} else {
searchEngineInput.style.display = 'none';
searchEngineInput.enabled = 'true';
localStorage.setItem('searchEngine', input);
}
}
static saveStuff() {
localStorage.setItem('blur', document.getElementById('blurRange').value); // this is better than inline onChange for performance
localStorage.setItem('greetingName', document.getElementById('greetingName').value);
localStorage.setItem('customBackground', document.getElementById('customBackground').value);
if (!document.getElementById('searchEngineInput').enabled === 'false') {
localStorage.setItem('customSearchEngine', document.getElementById('searchEngineInput').value);
}
window.location.reload();
}
static setDefaultSettings() {
localStorage.clear();
defaultSettings.forEach(element => localStorage.setItem(element.name, element.value));
// Set theme depending on user preferred
// if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) localStorage.setItem('darkTheme', true);
//else localStorage.setItem('darkTheme', false);
switch(detectBrowserLanguage()) {
case 'nl':
localStorage.setItem('language', 'nl');
break;
case 'no':
localStorage.setItem('language', 'no');
break;
case 'fr':
localStorage.setItem('language', 'fr');
break;
default:
localStorage.setItem('language', 'en');
}
// Finally we set this to true so it doesn't run the function on every load
localStorage.setItem('firstRun', true);
window.location.reload();
}
window.location.reload();
}

28
src/scss/_mixins.scss Normal file
View File

@ -0,0 +1,28 @@
// credit: https://joshbroton.com/quick-fix-sass-mixins-for-css-keyframe-animations/
@mixin animation($animate...) {
$max: length($animate);
$animations: '';
@for $i from 1 through $max {
$animations: #{$animations + nth($animate, $i)};
@if $i < $max {
$animations: #{$animations + ', '};
}
}
-webkit-animation: $animations;
-moz-animation: $animations;
animation: $animations;
}
@mixin keyframes($animationName) {
@-webkit-keyframes #{$animationName} {
@content;
}
@-moz-keyframes #{$animationName} {
@content;
}
@keyframes #{$animationName} {
@content;
}
}

31
src/scss/_variables.scss Normal file
View File

@ -0,0 +1,31 @@
@use 'sass:map';
$theme-colours: (
'gradient': linear-gradient(90deg, #ffb032 0%, #dd3b67 100%),
'main': rgba(242, 243, 244, 1),
'secondary': rgba(0, 0, 0, 1),
'main-text-color': rgba(242, 243, 244, 1),
);
$modal: (
'background': #fff,
'text': rgba(0, 0, 0, 1),
'tab-underline': rgba(204, 204, 204, 1),
'tab-underline-active': rgba(0, 0, 0, 1),
'border-radius': 12px,
);
$marketplace: (
'item-background': rgba(242, 243, 244, 1),
'product-information-backgroud': rgba(242, 243, 244, 1),
);
$button-colours: (
'confirm': rgba(46, 213, 115, 1),
'reset': rgba(255, 71, 87, 1),
'other': rgba(83, 82, 237, 1),
);
$main-parts: (
'shadow': 0 0 1rem 0 rgba(0, 0, 0, .2),
);

View File

@ -1,4 +1,6 @@
/* Imports */
@import 'variables';
@import 'mixins';
@import 'modules/clock';
@import 'modules/greeting';
@import 'modules/quote';
@ -8,6 +10,10 @@
@import 'modules/modal';
@import 'modules/settings';
@import 'modules/toast';
@import 'modules/marketplace';
@import 'modules/checkbox';
@import 'modules/buttons';
@import 'modules/welcome';
body {
background: #2f3640;
@ -18,6 +24,16 @@ body {
overflow: hidden;
}
* {
font-family: 'Lexend Deca' !important;
outline: none;
}
@font-face {
font-family: 'Lexend Deca';
src: url('/./fonts/LexendDeca-Regular.woff2') format('woff2');
}
#center {
margin-left: 2vw;
margin-right: 2vw;
@ -36,14 +52,14 @@ body {
}
::placeholder {
color: #ffffff;
color: map-get($theme-colours, 'main');
opacity: 1;
}
#root {
min-height: 100vh;
display: grid;
color: white;
color: map-get($theme-colours, 'main-text-color');
}
#backgroundImage {
@ -52,37 +68,17 @@ body {
background-repeat: no-repeat;
background-position: center;
background-attachment: fixed;
z-index: 0;
border: none;
transform: scale(1.1);
}
.fade-in {
-webkit-animation: fadein 2s;
animation: fadein 2s;
-moz-animation: fadein 2s;
@include animation('fadein 2s');
}
@-webkit-keyframes fadein {
@include keyframes(fadein) {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
@-moz-keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
@font-face {
font-family: 'Lexend Deca';
src: url('/./fonts/LexendDeca-Regular.woff2') format('woff2');
}
.backgroundEffects {
opacity: .7;
transition: ease 0.6s;
@ -90,15 +86,4 @@ body {
#searchEngine {
width: 130px;
}
.select-css {
background: none;
backdrop-filter: blur(10em);
height: 34px;
width: 100px;
border-image-slice: 1;
border-image-source: linear-gradient(180deg, #ffb032 0%, #dd3b67 100%);
border-left: 10px solid;
font-weight: 400;
}

View File

@ -0,0 +1,160 @@
%settingsButton {
text-align: center;
border: none;
transition: ease 0.33s;
color: map-get($theme-colours, "main");
cursor: pointer;
display: inline-block;
position: relative;
padding: 10px 30px;
font-size: 20px;
border-radius: 24px;
background: none;
&:hover {
outline: none;
}
&:active {
outline: none;
}
}
.apply {
@extend %settingsButton;
margin-right: 20px;
background-color: map-get($button-colours, "confirm");
border: 2px solid map-get($button-colours, "confirm");
&:hover {
border: 2px solid map-get($button-colours, "confirm");
color: map-get($button-colours, "confirm");
background: none;
}
}
.reset {
@extend %settingsButton;
background-color: map-get($button-colours, "reset");
border: 2px solid map-get($button-colours, "reset");
margin-left: 5px;
&:hover {
border: 2px solid map-get($button-colours, "reset");
color: map-get($button-colours, "reset");
background: none;
}
}
.close {
@extend %settingsButton;
padding: 10px 50px 10px 50px;
background-color: map-get($button-colours, "other");
border: 2px solid map-get($button-colours, "other");
&:hover {
color: map-get($button-colours, "other");
border: 2px solid map-get($button-colours, "other");
background: none;
}
}
.export,
.uploadbg,
.import {
@extend %settingsButton;
background-color: map-get($button-colours, "other");
border: 2px solid map-get($button-colours, "other");
color: map-get($theme-colours, "main");
&:hover {
color: map-get($button-colours, "other");
border: 2px solid map-get($button-colours, "other");
background: none;
}
}
.export,
.import {
float: right;
margin-left: 20px;
}
.storebutton {
cursor: pointer;
font-size: 18px;
float: right;
vertical-align: middle;
padding: 5px 30px;
background: none;
outline: none;
border: none;
border: 2px solid #2d3436;
border-radius: 24px;
font-family: 'Lexend Deca', sans-serif;
transition: ease 0.33s;
&:hover {
background: #2d3436;
color: map-get($theme-colours, "main");;
border: 2px solid #2d3436;
}
}
.dark .storebutton {
border: 2px solid map-get($theme-colours, "main");
color: map-get($theme-colours, "main");
&:hover {
background: map-get($theme-colours, "main");
color: #2d3436;
border: 2px solid map-get($theme-colours, "main");
}
}
#item >.removeFromMue {
border: 2px solid #ff4757;
color: #ff4757;
&:hover {
background: #ff4757;
color: map-get($theme-colours, "main");;
border: 2px solid #ff4757;
}
@extend .storebutton;
}
#item .addToMue,
#item .removeFromMue {
margin-top: 5px;
}
.addToMue {
@extend .storebutton;
}
.goToMarket {
float: none;
@extend .storebutton;
}
.sideload {
margin-top: 12px;
}
.seemore {
margin-top: 12px;
}
.seemore {
@extend %settingsButton;
background-color: #2d3436;
border: 2px solid #2d3436;
&:hover {
color: #2d3436;
border: 2px solid #2d3436;
background: none;
}
}

View File

@ -0,0 +1,3 @@
.MuiCheckbox-colorPrimary.Mui-checked {
color: map-get($button-colours, "reset") !important;
}

View File

@ -1,39 +1,20 @@
.clock {
font-size: 4em;
//text-shadow: 5px 2px rgba(0, 0, 0, 0.8);
margin: 0;
// text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
}
.ampm {
font-size: 0.5em;
}
.analogclock {
font-size: 4em;
.analogclock, .react-clock__face {
margin: 0 auto;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
border-radius: 100%;
box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.3);
border: 1px solid map-get($theme-colours, "main");
}
.react-clock__face {
border: none;
outline: none;
border: 3px #fff solid;
transition: 2s;
}
.react-clock__second-hand__body {
background: #e84118;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
transition: 2s;
}
.react-clock__hand__body {
background: #fff;
background: map-get($theme-colours, "main");;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
transition: 2s;
}
.react-clock__mark__body {
display: none;
}

View File

@ -21,7 +21,7 @@
cursor: pointer;
}
.MuiSvgIcon-root {
.navbar-container > svg, #backgroundCredits > svg {
font-size: calc(10px + 1.5vmin) !important;
position: absolute;
bottom: 2px;
@ -45,12 +45,13 @@
left: 10px;
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4));
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4));
.tooltiptext {
width: 200px;
display: inline-block;
visibility: hidden;
border-radius: 12px;
background-color: #fff;
background-color: map-get($theme-colours, "main");;
color: #000;
font-size: calc(10px + 1.2vmin);
position: absolute;
@ -63,11 +64,11 @@
transition: opacity 1s;
box-sizing: border-box;
}
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
&:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
}
#photographer, .locationicon {

View File

@ -0,0 +1,287 @@
.tab,
button.tablinks {
margin-top: -10px;
font-size: 24px;
background: none;
border: none;
outline: none;
color: map-get($modal, "text");
}
button.tablinks {
cursor: pointer;
padding: 6px;
border-radius: 12px;
padding: 10px 30px 10px 30px;
&:hover {
background: rgba(189, 195, 199, .075);
}
&:after {
content: "";
display: block;
margin: 0 auto;
width: 50%;
padding-top: 10px;
border-bottom: 3px solid map-get($modal, "tab-underline");
}
}
#item a {
color: map-get($button-colours, "other");
cursor: pointer;
&:hover {
opacity: 0.8;
}
}
#active:after {
content: "";
display: block;
margin: 0 auto;
width: 50%;
padding-top: 10px;
border-bottom: 3px solid map-get($modal, "tab-underline-active");
}
.dark {
.tab,
button.tablinks {
color: white;
}
#active:after {
border-image-slice: 1;
border-image-source: map-get($theme-colours, "gradient");
border-bottom: 3px solid;
}
}
.tab {
margin-left: -3px;
}
.items {
display: flex;
flex-flow: wrap;
align-items: left;
justify-content: left;
margin-top: -10px;
.item {
position: relative;
border-radius: 12px;
height: 80px;
width: 270px;
background: map-get($marketplace, "item-background");
transition: 0.5s;
cursor: pointer;
margin-right: 20px;
margin-bottom: 20px;
overflow: none;
img {
height: 100%;
width: auto;
border-radius: 12px 0 0 12px;
background: white;
}
h4 {
font-size: 20px;
line-height: 20px;
font-weight: 600;
}
img,
.details {
display: inline;
}
.details {
position: absolute;
left: 90px;
top: 15px;
img {
margin-left: 10px;
height: 15px;
}
}
p {
margin-top: 5px;
}
p.desc {
margin-top: -14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover {
transform: scale(1.1);
}
}
}
.dark .items .item {
background: #2d3436;
}
p.author {
margin-top: -5px;
}
#item {
display: none;
h1 {
font-size: 40px;
}
}
#item>h1,
#item>.MuiSvgIcon-root {
display: inline;
font-size: 35px !important;
}
p.description {
margin-top: 0px;
}
.emptyMessage {
margin: 0 auto;
text-align: center;
transform: translateY(30%);
svg {
font-size: 50px;
line-height: 0px;
margin: 0;
margin-bottom: -20px;
}
}
.backArrow {
position: relative;
cursor: pointer;
&:hover {
color: grey;
}
}
.informationContainer {
float: right;
}
.productInformation {
margin-bottom: 20px;
padding: 20px;
background: map-get($marketplace, "product-information-backgroud");
width: 200px;
border-radius: 12px;
li {
margin-left: -4px;
list-style: none;
}
li.header {
text-transform: uppercase;
color: #787878;
list-style: none;
margin-left: -5px;
}
}
.dark .productInformation {
background: #2d3436;
}
#item>img {
border-radius: 24px;
height: 200px;
width: auto;
}
#marketplace {
-webkit-animation: content 0.5s;
-moz-animation: content 0.5s;
-ms-animation: content 0.5s;
-o-animation: content 0.5s;
animation: content 0.5s;
}
@keyframes content {
from {
opacity: 0;
transform: translateY(5%);
}
to {
opacity: 1;
transform: translateY(0%);
}
}
.banner {
text-align: center;
background: #54a0ff;
border-radius: 24px;
padding: 10px;
margin: 0;
color: map-get($theme-colours, "main");
display: none !important;
}
.featured {
background: #2d3436;
margin-top: 20px;
border-radius: 24px;
padding: 50px;
color: map-get($theme-colours, "main");
button {
float: left;
margin-top: -20px;
border: 2px solid map-get($theme-colours, "main");
color: map-get($theme-colours, "main");
&:hover {
border: 2px solid map-get($theme-colours, "main");
background: map-get($theme-colours, "main");
color: #2d3436;
}
}
h1 {
margin-top: -20px;
}
}
.dark .featured {
background: black;
color: white;
}
.requires {
float: right;
}
#seemore {
display: none;
}
#seemore svg {
font-size: 35px !important;
margin-bottom: -30px;
}

View File

@ -1,6 +1,6 @@
.Modal {
color: #000;
background-color: #fff;
color: map-get($modal, "text");
background-color: map-get($modal, "background");
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
border: none;
opacity: 1;
@ -9,12 +9,17 @@
cursor: hand;
transition: 0.6s;
transition-timing-function: ease-in;
border-radius: 12px;
border-radius: map-get($modal, "border-radius");
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
&:focus {
outline: 0;
}
}
.modalLink {
color: #5352ed;
cursor: pointer;
@ -42,7 +47,6 @@
.ReactModal__Html--open,
.ReactModal__Body--open {
overflow: hidden;
/* prevents background page from scrolling when the modal is open */
}
@-webkit-keyframes zoom-in {
@ -73,11 +77,9 @@
}
}
.Overlay {
position: fixed;
z-index: 999999;
z-index: 100;
top: 0;
left: 0;
right: 0;
@ -96,25 +98,44 @@
}
.ReactModal__Content {
width: 500px;
max-width: 600px;
min-height: calc(100vh - 30vh);
max-height: calc(100vh - 10vh);
box-shadow: 0 0 30px 0 rgba(0, 0, 0, 0.25);
overflow-y: auto;
position: relative;
}
@media only screen and (max-height: 700px) {
.ReactModal__Content {
min-height: 500px;
max-height: calc(100vh - 30vh);
}
}
@media only screen and (min-height: 700px){
.ReactModal__Content {
min-height: 600px;
}
}
.content {
margin-top: -20px;
width: 900px;
h1 {
font-size: 35px;
}
p {
margin-top: -20px;
font-size: 16px;
}
.columns {
font-size: 15px;
li {
padding-left: 10px;
}
}
}
@ -149,4 +170,17 @@
100% {
-webkit-transform: scale(0);
}
}
.updateContent {
width: 400px;
padding: 5px;
.closeModal {
margin-top: 10px;
font-size: 45px;
}
img {
width: 100%;
height: auto;
}
}

View File

@ -5,21 +5,24 @@
cursor: pointer;
-webkit-filter: drop-shadow(0 0 6px rgba(0,0,0,.3));
filter: drop-shadow(0 0 6px rgba(0,0,0,.3));
top: 50px;
top: 20px;
svg {
transition: ease 0.2s;
font-size: calc(10px + 1.5vmin) !important;
}
&:hover {
svg {
transform: scale(1.1);
color: #fff;
color: map-get($theme-colours, "main");;
}
}
}
.navbar1 {
@extend %navbar;
right: 0px;
right: 20px;
svg {
-webkit-backface-visibility: hidden;
-webkit-transform: translateZ(0) scale(1.0, 1.0);
@ -30,7 +33,7 @@
.navbar2 {
@extend %navbar;
right: 50px;
right: 60px;
}
.navbar3 {

View File

@ -1,6 +1,6 @@
.quote {
font-size: 0.8em;
text-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
@media screen and (min-width: 600px) {
@ -30,7 +30,10 @@
float: middle;
margin: 0 auto;
text-align: right;
// transform: rotate(45deg);
transition: ease 0.2s !important;
&:hover {
transform: scale(1.1);
}
}
i.material-icons,
@ -41,7 +44,7 @@ h1.quoteauthor {
button.copyButton {
background: transparent;
border: none;
color: #fff;
color: map-get($theme-colours, "main");;
padding: 20px 20px;
text-align: center;
text-decoration: none;

View File

@ -5,26 +5,35 @@
display: flex;
flex-direction: row;
display: block;
color: #ffff;
font-family: 'Lexend Deca', sans-serif;;
color: map-get($theme-colours, "main-text-color");
input[type=text] {
width: 140px;
margin-left: 12px;
border-radius: 24px;
font-size: calc(5px + 1.2vmin);
background: transparent;
border: 2px solid #ffff;
padding: 10px;
color: #ffff;
border: none;
color: map-get($theme-colours, "main-text-color");
position: absolute;
box-shadow: 0 0 25px rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.1);
-webkit-transition: width 0.5s ease-in-out;
transition: width 0.5s ease-in-out;
&:focus {
width: 400px;
background-color: rgba(0, 0, 0, .3);
}
}
.MuiSvgIcon-root {
margin-top: 4px;
font-size: 30px;
filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.3));
cursor: pointer;
}
}
.input.searchtext {
border: none;
}
.searchbarform {
display: flex;
flex-direction: row;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .25);
}

View File

@ -1,8 +1,5 @@
$gradient: linear-gradient(90deg, #ffb032 0%, #dd3b67 100%);
.switch {
position: relative;
/* display: inline-block; */
width: 60px;
height: 34px;
float: right;
@ -35,7 +32,7 @@ $gradient: linear-gradient(90deg, #ffb032 0%, #dd3b67 100%);
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
background-color: #fff;
-webkit-transition: 0.4s;
transition: 0.4s;
border-radius: 50%;
@ -52,14 +49,13 @@ input {
padding: 0.5rem 1rem;
box-sizing: border-box;
border-image-slice: 1;
border-image-source: $gradient;
border-image-source: map-get($theme-colours, "gradient");
outline: none;
font-family: 'Lexend Deca', sans-serif;
background: transparent;
}
&:checked + .slider {
background: $gradient;
&:checked+.slider {
background: map-get($theme-colours, "gradient");
&:before {
-webkit-transform: translateX(26px);
@ -68,7 +64,7 @@ input {
}
}
&:focus + .slider {
&:focus+.slider {
box-shadow: 0 0 1px #e67e22;
}
}
@ -83,13 +79,16 @@ input {
background: #34495e;
}
h4, .switch, .expandIcons {
h4,
.switch,
.expandIcons {
display: inline;
font-size: 1.4em;
font-weight: 100;
}
h4, #engines {
h4,
#engines {
display: inline;
}
@ -97,101 +96,15 @@ h4, #engines {
margin-bottom: 20px;
}
%settingsButton {
text-align: center;
border: none;
transition: 0.25s;
color: #fff;
cursor: pointer;
display: inline-block;
box-shadow: 20px #000;
position: relative;
padding: 15px;
width: 120px;
font-size: 1.1em;
font-family: 'Lexend Deca', sans-serif;
&:hover {
transform: translateY(-0.10em);
outline: none;
}
&:before {
content: '';
position: absolute;
width: 0%;
top: 0;
left: 0;
height: 100%;
transition: 0.3s linear;
z-index: -1;
color: #fff;
}
&:hover:before {
width: 100%;
}
&:active {
outline: none;
}
}
.apply {
@extend %settingsButton;
background-color: #dd3b67;
&:before {
background: $gradient;
}
}
.import {
float: right;
@extend %settingsButton;
background-color: #dd3b67;
margin-right: 5px;
&:before {
background: $gradient;
}
}
.export {
float: right;
@extend %settingsButton;
background-color: #dd3b67;
&:before {
background: $gradient;
}
}
.uploadbg {
@extend %settingsButton;
background-color: #dd3b67;
width: auto;
height: auto;
padding: 5px 18px;
&:before {
background: $gradient;
}
}
.reset {
@extend %settingsButton;
background-color: #ffb032;
margin-left: 5px;
&:before {
background: linear-gradient(90deg, #dd3b67 0%, #ffb032 100%);
}
}
.expandIcons {
position: relative;
font-size: 25px;
vertical-align: middle;
display: inline-flex;
}
h4,
.expandIcons {
cursor: pointer;
}
@ -202,17 +115,28 @@ h4, #engines {
border-width: 5px;
border-image-source: linear-gradient(to bottom, #ffb032 0%, #dd3b67 100%);
> p {
p {
margin: 0;
padding: 0;
}
select {
margin-left: 20px;
}
input[type=text],
input[type=range],
p,
button {
margin-top: 10px;
}
}
ul {
padding-left: 5px;
margin: 0;
> label {
>label {
vertical-align: middle;
}
}
@ -238,7 +162,7 @@ li {
width: 25px;
height: 25px;
border-radius: 12px;
background: $gradient;
background: map-get($theme-colours, "gradient");
cursor: pointer;
}
@ -246,7 +170,7 @@ li {
width: 25px;
height: 25px;
border-radius: 12px;
background: $gradient;
background: map-get($theme-colours, "gradient");
cursor: pointer;
}
}
@ -255,7 +179,7 @@ input[type=color] {
border-radius: 100%;
height: 30px;
width: 30px;
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, .2);
box-shadow: map-get($main-parts, "shadow");
border: none;
outline: none;
-webkit-appearance: none;
@ -280,15 +204,16 @@ input[type=checkbox] {
padding-left: 7px;
}
select#language {
float: right;
select {
background: none;
backdrop-filter: blur(10em);
height: 34px;
width: 100px;
width: 120px;
border-image-slice: 1;
border-image-source: linear-gradient(180deg, #ffb032 0%, #dd3b67 100%);
border-left: 10px solid;
font-weight: 400;
font-family: 'Lexend Deca', sans-serif;
color: map-get($modal, "text");
}
select#language {
float: right;
}

View File

@ -1,5 +1,4 @@
.Toastify__toast-body {
font-family: 'Lexend Deca', sans-serif !important;
color: #000 !important;
font-size: 16px !important;
padding: 15px 20px !important;

View File

@ -0,0 +1,42 @@
.welcomeModalText {
line-height: 2px;
h2.subtitle {
font-size: 24px;
color: #535353;
text-transform: uppercase;
}
h1.welcometitle {
font-size: 50px;
}
}
.welcomeContent {
text-align: center;
padding: 25px;
a {
text-decoration: none;
line-height: 20px !important;
color: #5352ED;
cursor: pointer;
&:hover {
opacity: 0.8;
}
}
img.icon, svg {
margin-top: -12px;
padding: 10px;
cursor: pointer;
transition: ease 0.2s;
&:hover {
transform: scale(1.1);
}
}
p {
margin-top: 0.7rem;
line-height: 1em;
}
img, svg {
height: 24px;
width: auto;
}
}

View File

@ -9,15 +9,19 @@
},
"credit": "Photo by",
"search": "Search",
"modals": {
"title": "Options",
"settings": "Settings",
"addons": "My Add-ons",
"marketplace": "Marketplace"
},
"settings": {
"title": "Settings",
"description": "Edit different components to make Mue your new tab.",
"time": {
"title": "Time",
"seconds": "Seconds",
"twentyfourhour": "24 Hour",
"ampm": "AM/PM (12 hour)",
"zero": "Extra Zero",
"zero": "Zero-padded",
"analog": "Analog"
},
"greeting": {
@ -68,7 +72,59 @@
"loading": "Loading..."
},
"toasts": {
"quote": "Quote Copied!",
"reset": "Reset successfully!"
"quote": "Quote Copied",
"reset": "Reset successfully",
"installed": "Successfully installed",
"removed": "Successfully removed",
"error": "Something went wrong"
},
"marketplace": {
"photo_packs": "Photo Packs",
"quote_packs": "Quote Packs",
"preset_settings": "Preset Settings",
"themes": "Themes",
"product": {
"overview": "Overview",
"information": "Information",
"last_updated": "Last Updated",
"version": "Version",
"author": "Author",
"notice": {
"title": "Notice",
"description": "In order for the change to take place, the page must be refreshed."
},
"buttons": {
"addtomue": "Add To Mue",
"remove": "Remove"
}
},
"offline": {
"title": "Looks like you're offline",
"description": "Please connect to the internet."
},
"see_more": "See More"
},
"addons": {
"added": "Added",
"product": {
"overview": "Overview",
"information": "Information",
"last_updated": "Last Updated",
"version": "Version",
"author": "Author",
"notice": {
"title": "Notice",
"description": "In order for the change to take place, the page must be refreshed."
},
"buttons": {
"addtomue": "Add To Mue",
"remove": "Remove"
}
},
"empty": {
"title": "It's empty here",
"description": "Head to the marketplace to add some.",
"button": "Take me there"
}
}
}

View File

@ -1,74 +1,130 @@
{
"greeting": {
"morning": "Good Morning",
"afternoon": "Good Afternoon",
"evening": "Good Evening",
"christmas": "Merry Christmas",
"newyear": "Happy new year",
"halloween": "Happy Halloween"
"morning": "Bonjour",
"afternoon": "Bonne après-midi",
"evening": "Bonsoir",
"christmas": "Bonne Noël",
"newyear": "Bonne année",
"halloween": "Joyeux Halloween"
},
"credit": "Photo par",
"search": "Trouver",
"modals": {
"title": "Options",
"settings": "Parametres",
"addons": "Mes Options",
"marketplace": "Marché"
},
"credit": "Photo by",
"search": "Search",
"settings": {
"title": "Settings",
"description": "Edit different components to make Mue your new tab.",
"time": {
"title": "Time",
"title": "Temps",
"seconds": "Seconds",
"twentyfourhour": "24 Hour",
"twentyfourhour": "24 Heures",
"ampm": "AM/PM (12 hour)",
"zero": "Extra Zero",
"analog": "Analog"
"zero": "Zero-padded",
"analog": "Analogue"
},
"greeting": {
"title": "Greeting",
"events": "Events",
"default": "Default Greeting Message",
"name": "Name for greeting"
"title": "Salutation",
"events": "Événements",
"default": "Défaut Salutation",
"name": "Nom Pour Salutation"
},
"quote": {
"title": "Quote",
"copy": "Copy Button"
"title": "Citation",
"copy": "Au bouton copie"
},
"background": {
"title": "Background",
"API": "Background API",
"blur": "Adjust Blur",
"customURL": "Custom Background URL",
"custombackground": "Custom Background",
"customcolour": "Custom Background Colour"
"title": "Fond",
"API": "Fond API",
"blur": "Ajuster le flou",
"customURL": "URL D'arrière-plan personnalisée",
"custombackground": "D'arrière-plan personnalisée",
"customcolour": "Couleur d'arrière-plan personnalisée"
},
"searchbar": {
"title": "Search Bar",
"searchengine": "Search Engine"
"title": "Barre de Recherche",
"searchengine": "Moteur de recherche"
},
"offline": "Offline Mode",
"offline": "Mode Hors-Ligne",
"experimental": {
"title": "Experimental",
"webp": "Enable WebP",
"dark": "Dark Theme",
"title": "Expérimental",
"webp": "Activer WebP",
"dark": "Thème sombre",
"animations": "Animations"
},
"language": "Language",
"apply": "Apply",
"language": "Langue",
"apply": "Appliquer",
"reset": "Reset",
"import": "Import",
"export": "Export"
"import": "Importer",
"export": "Exporter"
},
"update": {
"title": "Update",
"title": "Actualiser",
"offline": {
"title": "Offline",
"description": "Cannot get update logs while in offline mode"
"title": "Hors ligne",
"description": "Peut pas se connecter à Internet"
},
"error": {
"title": "Error",
"title": "Erreur",
"content": "Could not connect to the server"
},
"loading": "Loading..."
"loading": "Chargement..."
},
"toasts": {
"quote": "Quote Copied!",
"reset": "Reset successfully!"
"quote": "Citation Copié",
"reset": "Réinitaliser avec succès",
"installed": "Installé avec succès",
"removed": "Enlevée avec succès",
"error": "Quelque chose a mal tourné."
},
"marketplace": {
"photo_packs": "Des Photos Packs",
"quote_packs": "Des Citation Packs",
"preset_settings": "Paramètres de prédéterminée",
"themes": "Thème",
"product": {
"overview": "Aperçu",
"information": "Information",
"last_updated": "Dernière mise à jour",
"version": "Version",
"author": "Auteur",
"notice": {
"title": "Écriteau",
"description": "S'il vous plait, recharger la page."
},
"buttons": {
"addtomue": "Ajouter au Mue",
"remove": "Enlever"
}
},
"offline": {
"title": "Vous êtes hors ligne",
"description": "Veuillez vous connecter à l'internet."
},
"see_more": "Voir de Plus"
},
"addons": {
"added": "Ajoutée",
"product": {
"overview": "Aperçu",
"information": "Informations",
"last_updated": "Dernière mise à jour",
"version": "Version",
"author": "Auteure",
"notice": {
"title": "Écriteau",
"description": "S'il vous plait, recharger la page."
},
"buttons": {
"addtomue": "Ajouter au Mue",
"remove": "Enlever"
}
},
"empty": {
"title": "Il est vide ici",
"description": "Aller su le marché pour en ajouter.",
"button": "Naviguer vers le marché"
}
}
}

View File

@ -1,74 +1,130 @@
{
"greeting": {
"morning": "Good Morning",
"afternoon": "Good Afternoon",
"evening": "Good Evening",
"christmas": "Merry Christmas",
"newyear": "Happy new year",
"halloween": "Happy Halloween"
"morning": "Goedemorgen",
"afternoon": "Goedemiddag",
"evening": "Goedenavond",
"christmas": "Fijne kerstdagen",
"newyear": "Gelukkig nieuw jaar",
"halloween": "Fijne halloween"
},
"credit": "Afbeelding door",
"search": "Zoeken",
"modals": {
"title": "Opties",
"settings": "Instellingen",
"addons": "Mijn Extensies",
"marketplace": "Marktplaats"
},
"credit": "Photo by",
"search": "Search",
"settings": {
"title": "Settings",
"description": "Edit different components to make Mue your new tab.",
"time": {
"title": "Time",
"seconds": "Seconds",
"twentyfourhour": "24 Hour",
"ampm": "AM/PM (12 hour)",
"zero": "Extra Zero",
"analog": "Analog"
"title": "Tijd",
"seconds": "Seconden",
"twentyfourhour": "24 uurs",
"ampm": "AM/PM (12 uurs)",
"zero": "Nul Opvulling",
"analog": "Analoog"
},
"greeting": {
"title": "Greeting",
"events": "Events",
"default": "Default Greeting Message",
"name": "Name for greeting"
"title": "Groeten",
"events": "Evenementen",
"default": "Standaard begroetingsnaam",
"name": "Naam Voor Groet"
},
"quote": {
"title": "Quote",
"copy": "Copy Button"
"title": "Citaat",
"copy": "Kopieer Knop"
},
"background": {
"title": "Background",
"API": "Background API",
"blur": "Adjust Blur",
"customURL": "Custom Background URL",
"custombackground": "Custom Background",
"customcolour": "Custom Background Colour"
"title": "Achtergrond",
"API": "Achtergrond API",
"blur": "Onscherpte Aanpassen",
"customURL": "Aangepaste Achtergrond URL",
"custombackground": "Aangepaste Achtergrond",
"customcolour": "Aangepaste Achtergrondkleur"
},
"searchbar": {
"title": "Search Bar",
"searchengine": "Search Engine"
"title": "Zoekbalk",
"searchengine": "Zoekmachine"
},
"offline": "Offline Mode",
"offline": "Offline Modus",
"experimental": {
"title": "Experimental",
"webp": "Enable WebP",
"dark": "Dark Theme",
"animations": "Animations"
"title": "Experimenteel",
"webp": "WebP Gebruiken",
"dark": "Donker Thema",
"animations": "Animaties"
},
"language": "Language",
"apply": "Apply",
"reset": "Reset",
"import": "Import",
"export": "Export"
"language": "Taal",
"apply": "Toepassen",
"reset": "Resetten",
"import": "Importeren",
"export": "Exporteren"
},
"update": {
"title": "Update",
"title": "Veranderingen",
"offline": {
"title": "Offline",
"description": "Cannot get update logs while in offline mode"
"description": "Kan veranderingen logboek niet verkrijgen in offline modus"
},
"error": {
"title": "Error",
"content": "Could not connect to the server"
"title": "Fout",
"content": "Kon niet verbinden met de servers"
},
"loading": "Loading..."
"loading": "Aan het laden..."
},
"toasts": {
"quote": "Quote Copied!",
"reset": "Reset successfully!"
"quote": "Quote Copied",
"reset": "Reset successfully",
"installed": "Successvol geïnstalleerd",
"removed": "Successvol verwijderd",
"error": "Iets is verkeerd gegaan"
},
"marketplace": {
"photo_packs": "Afbeelding Pakketten",
"quote_packs": "Citaat Pakketten",
"preset_settings": "Vooraf ingestelde instellingen",
"themes": "Themas",
"product": {
"overview": "Overzicht",
"information": "Informatie",
"last_updated": "Laatst Geüpdatet",
"version": "Versie",
"author": "Auteur",
"notice": {
"title": "Aankondiging",
"description": "De veranderingen gaan in zodra je de pagina herlaad."
},
"buttons": {
"addtomue": "Voeg aan Mue toe",
"remove": "Verwijder"
}
},
"offline": {
"title": "Het ziet er uit dat je offline bent",
"description": "Verbind met het internet."
},
"see_more": "Zie Meer"
},
"addons": {
"added": "Toegevoegd",
"product": {
"overview": "Overzicht",
"information": "Informatie",
"last_updated": "Laatst Geüpdatet",
"version": "Versie",
"author": "Auteur",
"notice": {
"title": "Aankondiging",
"description": "De veranderingen gaan in zodra je de pagina herlaad."
},
"buttons": {
"addtomue": "Voeg aan Mue toe",
"remove": "Verwijder"
}
},
"empty": {
"title": "Het is hier leeg",
"description": "Ga naar de marktplaats om een paar extensies toe te veogen.",
"button": "Breng me naar de marktplaats"
}
}
}
}

130
src/translations/no.json Normal file
View File

@ -0,0 +1,130 @@
{
"greeting": {
"morning": "God Morgen",
"afternoon": "God Ettermiddag",
"evening": "God Kveld",
"christmas": "God Jul",
"newyear": "Godt Nyttår",
"halloween": "Ha en god Halloween"
},
"credit": "Bilde av",
"search": "Søk",
"modals": {
"title": "Options",
"settings": "Instillinger",
"addons": "Mine add-ons",
"marketplace": "Markedsplass"
},
"settings": {
"time": {
"title": "Tid",
"seconds": "Sekunder",
"twentyfourhour": "24 Timer",
"ampm": "AM/PM (12 Timer)",
"zero": "Ekstra Null",
"analog": "Analog"
},
"greeting": {
"title": "Hallo",
"events": "Planer",
"default": "Standard Hallo Melding",
"name": "Navn for Hallo"
},
"quote": {
"title": "Sitat",
"copy": "Kopier knapp"
},
"background": {
"title": "Bakgrunn",
"API": "Bakgrunn API",
"blur": "Juster Blur",
"customURL": "Personlig bakgrunn URL",
"custombackground": "Personlig bakgrunn",
"customcolour": "Personlig Bakgrunn Farge"
},
"searchbar": {
"title": "Søkebar",
"searchengine": "Søkemotor"
},
"offline": "Frakoblet Modus",
"experimental": {
"title": "Eksperimental",
"webp": "Skru på WebP",
"dark": "Mørkt tema",
"animations": "Animasjoner"
},
"language": "Språk",
"apply": "Bruk",
"reset": "Nullstill",
"import": "Importer",
"export": "Eksporter"
},
"update": {
"title": "Oppdater",
"offline": {
"title": "Offline",
"description": "Kan ikke få oppdatering loggene når i offline modus."
},
"error": {
"title": "Error",
"content": "Kan ikke koble til serveren."
},
"loading": "Laster..."
},
"toasts": {
"quote": "Sitat kopiert",
"reset": "Tilbakestillte vellykket",
"installed": "Installert",
"removed": "Fjernet",
"error": "Noe gikk galt"
},
"marketplace": {
"photo_packs": "Bilde pakker",
"quote_packs": "Sitat pakker",
"preset_settings": "Preset instillinger",
"themes": "Themes",
"product": {
"overview": "Oversikt",
"information": "Informasjon",
"last_updated": "Sist oppdatert",
"version": "Versjon",
"author": "Forfatter",
"notice": {
"title": "Vær oppmerksom",
"description": "For at endringen skal ta skje, må siden relastes."
},
"buttons": {
"addtomue": "Legg Til Mue",
"remove": "Fjern"
}
},
"offline": {
"title": "Ser ut som at du er offiline.",
"description": "Vær så snill, koble til internettet."
},
"see_more": "Se mer"
},
"addons": {
"added": "Lagt til",
"product": {
"overview": "Oversikt",
"information": "Informasjon",
"last_updated": "Sist oppdatert",
"version": "Versjon",
"author": "Forfatter",
"notice": {
"title": "Vær oppmerksom",
"description": "For at endringen skal ta skje, må siden relastes."
},
"buttons": {
"addtomue": "Legg Til Mue",
"remove": "Fjern"
}
},
"empty": {
"title": "Det er tomt her.",
"description": "Gå til markedsplassen å legg til noe.",
"button": "Ta meg der"
}
}
}

74
src/translations/ru.json Normal file
View File

@ -0,0 +1,74 @@
{
"greeting": {
"morning": "Доброе утро",
"afternoon": "Добрый день",
"evening": "Добрый вечер",
"christmas": "С Рождеством",
"newyear": "С Новым Годом",
"halloween": "Счастливого Хэллоуина"
},
"credit": "Фото",
"search": "Поиск",
"settings": {
"title": "Настройки",
"description": "Измените компоненты Mue по своему вкусу.",
"time": {
"title": "Время",
"seconds": "Секунды",
"twentyfourhour": "24 Часа",
"ampm": "AM/PM (12 часов)",
"zero": "Дополнительный ноль",
"analog": "Аналоговое"
},
"greeting": {
"title": "Приветствие",
"events": "События",
"default": "Сообщение с приветствием по-умолчанию",
"name": "Имя для приветствия"
},
"quote": {
"title": "Цитата",
"copy": "Кнопка копирования"
},
"background": {
"title": "Фон",
"API": "API для фона",
"blur": "Размытие",
"customURL": "Ссылка для пользовательского фона",
"custombackground": "Пользовательский фон",
"customcolour": "Пользовательский цвет фона"
},
"searchbar": {
"title": "Панель поиска",
"searchengine": "Поисковый движок"
},
"offline": "Офлайн режим",
"experimental": {
"title": "Экспериментальные настройки",
"webp": "Включить WebP",
"dark": "Тёмная тема",
"animations": "Анимации"
},
"language": "Язык",
"apply": "Применить",
"reset": "Сбросить",
"import": "Импорт",
"export": "Экспорт"
},
"update": {
"title": "Обновление",
"offline": {
"title": "Офлайн",
"description": "Не удалось получить журнал обновления в офлайн режиме"
},
"error": {
"title": "Ошибка",
"content": "Не удалось подключиться к серверу"
},
"loading": "Загрузка..."
},
"toasts": {
"quote": "Цитата скопирована!",
"reset": "Сброшено!"
}
}