mirror of
https://github.com/mue/mue.git
synced 2026-07-12 10:46:12 +02:00
refactor: new background component
This commit is contained in:
42
src/App.jsx
42
src/App.jsx
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
|
||||
import Background from './components/widgets/background/Background';
|
||||
import Widgets from './components/widgets/Widgets';
|
||||
import PhotoInformation from './components/widgets/background/PhotoInformation';
|
||||
import Navbar from './components/widgets/navbar/Navbar';
|
||||
|
||||
import SettingsFunctions from './modules/helpers/settings';
|
||||
@@ -23,7 +22,9 @@ export default class App extends React.PureComponent {
|
||||
mainModal: false,
|
||||
updateModal: false,
|
||||
welcomeModal: false,
|
||||
feedbackModal: false
|
||||
feedbackModal: false,
|
||||
toastDisplayTime: localStorage.getItem('toastDisplayTime') || 2500,
|
||||
overlayClassList: (localStorage.getItem('animations') === 'true') ? 'Overlay modal-animation' : 'Overlay'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,6 +41,20 @@ export default class App extends React.PureComponent {
|
||||
|
||||
SettingsFunctions.loadSettings();
|
||||
|
||||
// dark theme support for modals and info card
|
||||
let modalClassList = 'Modal';
|
||||
let tooltipClassList = 'infoCard';
|
||||
|
||||
if ((localStorage.getItem('brightnessTime') && new Date().getHours() > 18) || localStorage.getItem('darkTheme') === 'true') {
|
||||
modalClassList += ' dark';
|
||||
tooltipClassList += ' dark';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
modalClassList: modalClassList,
|
||||
tooltipClassList: tooltipClassList
|
||||
});
|
||||
|
||||
// These lines of code prevent double clicking the page or pressing CTRL + A from highlighting the page
|
||||
document.addEventListener('mousedown', (event) => {
|
||||
if (event.detail > 1) {
|
||||
@@ -69,34 +84,21 @@ export default class App extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
// dark theme support for modals and info card
|
||||
let modalClassList = 'Modal';
|
||||
let tooltipClassList = 'infoCard';
|
||||
|
||||
if ((localStorage.getItem('brightnessTime') && new Date().getHours() > 18) || localStorage.getItem('darkTheme') === 'true') {
|
||||
modalClassList += ' dark';
|
||||
tooltipClassList += ' dark';
|
||||
}
|
||||
|
||||
const overlayClassList = (localStorage.getItem('animations') === 'true') ? 'Overlay modal-animation' : 'Overlay';
|
||||
const toastDisplayTime = localStorage.getItem('toastDisplayTime') || 2500;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Background/>
|
||||
<ToastContainer position='bottom-right' autoClose={toastDisplayTime} newestOnTop={true} closeOnClick pauseOnFocusLoss/>
|
||||
<Background photoInformationClass={this.state.tooltipClassList}/>
|
||||
<ToastContainer position='bottom-right' autoClose={this.state.toastDisplayTime} newestOnTop={true} closeOnClick pauseOnFocusLoss/>
|
||||
<div id='center'>
|
||||
<Navbar openModal={(modal) => this.setState({ [modal]: true })}/>
|
||||
<Widgets/>
|
||||
<PhotoInformation className={tooltipClassList}/>
|
||||
<React.Suspense fallback={renderLoader()}>
|
||||
<Modal closeTimeoutMS={300} id={'modal'} onRequestClose={() => this.setState({ mainModal: false })} isOpen={this.state.mainModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
<Modal closeTimeoutMS={300} id={'modal'} onRequestClose={() => this.setState({ mainModal: false })} isOpen={this.state.mainModal} className={this.state.modalClassList} overlayClassName={this.state.overlayClassList} ariaHideApp={false}>
|
||||
<Main modalClose={() => this.setState({ mainModal: false })} />
|
||||
</Modal>
|
||||
<Modal onRequestClose={() => this.setState({ updateModal: false })} isOpen={this.state.updateModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
<Modal onRequestClose={() => this.setState({ updateModal: false })} isOpen={this.state.updateModal} className={this.state.modalClassList} overlayClassName={this.state.overlayClassList} ariaHideApp={false}>
|
||||
<Update modalClose={() => this.setState({ updateModal: false })} />
|
||||
</Modal>
|
||||
<Modal onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
<Modal onRequestClose={() => this.closeWelcome()} isOpen={this.state.welcomeModal} className={this.state.modalClassList} overlayClassName={this.state.overlayClassList} ariaHideApp={false}>
|
||||
<Welcome modalClose={() => this.closeWelcome()} />
|
||||
</Modal>
|
||||
{/* <Modal onRequestClose={() => this.setState({ feedbackModal: false })} isOpen={this.state.feedbackModal} className={modalClassList} overlayClassName={overlayClassList} ariaHideApp={false}>
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import PhotoInformation from './PhotoInformation';
|
||||
|
||||
import * as Constants from '../../../modules/constants';
|
||||
|
||||
import './scss/index.scss';
|
||||
|
||||
export default class Background extends React.PureComponent {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.state = {
|
||||
style: '',
|
||||
url: '',
|
||||
video: false,
|
||||
photoInfo: {
|
||||
hidden: false,
|
||||
credit: '',
|
||||
location: 'N/A',
|
||||
camera: 'N/A',
|
||||
resolution: 'N/A'
|
||||
}
|
||||
};
|
||||
this.language = window.language.widgets.background;
|
||||
}
|
||||
|
||||
gradientStyleBuilder(gradientSettings) {
|
||||
const { type, angle, gradient } = gradientSettings;
|
||||
let style = `background: ${gradient[0].colour};`;
|
||||
@@ -13,55 +32,32 @@ export default class Background extends React.PureComponent {
|
||||
const stepStyles = gradient.map(g => ` ${g.colour} ${g.stop}%`).join();
|
||||
style += ` background: ${type}-gradient(${(type === 'linear' ? (`${angle}deg,`) : '')}${stepStyles})`;
|
||||
}
|
||||
return style;
|
||||
this.setState({
|
||||
style: style
|
||||
});
|
||||
}
|
||||
|
||||
// Sets the attributes of the background image
|
||||
setBackground(url, colour, credit) {
|
||||
let gradientSettings = '';
|
||||
try {
|
||||
gradientSettings = JSON.parse(colour);
|
||||
} catch (e) {
|
||||
const hexColorRegex = /#[0-9a-fA-F]{6}/s;
|
||||
if (hexColorRegex.exec(colour)) {
|
||||
// Colour use to be simply a hex colour or a NULL value before it was a JSON object. This automatically upgrades the hex colour value to the new standard. (NULL would not trigger an exception)
|
||||
gradientSettings = { "type": "linear", "angle": "180", "gradient": [{ "colour": colour, "stop": 0 }] };
|
||||
localStorage.setItem('customBackgroundColour', JSON.stringify(gradientSettings));
|
||||
}
|
||||
}
|
||||
|
||||
const background = typeof gradientSettings === 'object' && gradientSettings !== null ? this.gradientStyleBuilder(gradientSettings) : `background-image: url(${url})`;
|
||||
|
||||
setBackground() {
|
||||
// Brightness
|
||||
let brightness = localStorage.getItem('brightness');
|
||||
if (localStorage.getItem('brightnessTime') && new Date().getHours() > 18) {
|
||||
brightness = 75;
|
||||
}
|
||||
|
||||
document.querySelector('#backgroundImage').setAttribute(
|
||||
'style',
|
||||
`${background}; -webkit-filter: blur(${localStorage.getItem('blur')}px) brightness(${brightness}%);`
|
||||
);
|
||||
|
||||
// Hide the credit
|
||||
if (credit === 'false' && document.querySelector('#credits')) {
|
||||
document.querySelector('#credits').style.display = 'none';
|
||||
if (this.state.url !== '') {
|
||||
document.querySelector('#backgroundImage').setAttribute(
|
||||
'style',
|
||||
`background-image: url(${this.state.url}); -webkit-filter: blur(${localStorage.getItem('blur')}px) brightness(${brightness}%);`
|
||||
);
|
||||
} else {
|
||||
document.querySelector('#backgroundImage').setAttribute(
|
||||
'style',
|
||||
`${this.state.style}; -webkit-filter: blur(${localStorage.getItem('blur')}px) brightness(${brightness}%);`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCredit(photographer, unsplash, url) {
|
||||
let credit = photographer;
|
||||
if (unsplash){
|
||||
credit = `<a href='${url}' class='creditlink'>${photographer}</a> on <a href='https://unsplash.com/?utm_source=mue&utm_medium=referral' class='creditlink'>Unsplash</a>`;
|
||||
}
|
||||
|
||||
document.querySelector('#photographer').insertAdjacentHTML('beforeend', ` ${credit}`); // Append credit
|
||||
document.getElementById('credit').textContent = credit;
|
||||
document.getElementById('photographerCard').textContent = credit;
|
||||
}
|
||||
|
||||
// Handles setting the background if the user is offline
|
||||
doOffline() {
|
||||
offlineBackground() {
|
||||
const offlineImages = require('./offline_images.json');
|
||||
|
||||
// Get all photographers from the keys in offlineImages.json
|
||||
@@ -74,126 +70,139 @@ export default class Background extends React.PureComponent {
|
||||
Math.floor(Math.random() * offlineImages[photographer].photo.length)
|
||||
];
|
||||
|
||||
const url = `./offline-images/${randomImage}.jpg`;
|
||||
|
||||
this.setBackground(url);
|
||||
this.setCredit(photographer);
|
||||
|
||||
// Hide the location icon
|
||||
//document.querySelector('#backgroundCredits').style.display = 'none';
|
||||
this.setState({
|
||||
url: `./offline-images/${randomImage}.jpg`,
|
||||
photoInfo: {
|
||||
hidden: true,
|
||||
credit: photographer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async determineMode() {
|
||||
const offlineMode = localStorage.getItem('offlineMode');
|
||||
|
||||
const photoPack = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour');
|
||||
const customBackground = localStorage.getItem('customBackground');
|
||||
// Main background getting function
|
||||
async getBackground() {
|
||||
if (localStorage.getItem('offlineMode') === 'true') {
|
||||
return this.offlineBackground();
|
||||
}
|
||||
|
||||
// favourite button
|
||||
const favourited = JSON.parse(localStorage.getItem('favourite'));
|
||||
|
||||
if (favourited) {
|
||||
if (offlineMode === 'true') {
|
||||
return this.doOffline();
|
||||
}
|
||||
return this.setState({
|
||||
url: favourited.url,
|
||||
photoInfo: {
|
||||
credit: favourited.credit,
|
||||
location: favourited.location
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.setBackground(favourited.url, null, 'true');
|
||||
this.setCredit(favourited.credit);
|
||||
document.getElementById('location').textContent = favourited.location;
|
||||
} else if (photoPack) {
|
||||
if (offlineMode === 'true') {
|
||||
return this.doOffline();
|
||||
}
|
||||
|
||||
const randomPhoto = photoPack[Math.floor(Math.random() * photoPack.length)];
|
||||
|
||||
this.setBackground(randomPhoto.url.default, null, randomPhoto.photographer);
|
||||
this.setCredit(randomPhoto.photographer);
|
||||
document.getElementById('location').textContent = randomPhoto.location;
|
||||
} else if (customBackgroundColour !== 'Disabled' && customBackgroundColour !== '') {
|
||||
this.setBackground(null, customBackgroundColour, 'false');
|
||||
} else if (customBackground !== '') {
|
||||
if (customBackground.includes('.mp4') || customBackground.includes('.webm') || customBackground.includes('.ogg')) {
|
||||
document.getElementById('backgroundImage').innerHTML = `
|
||||
<video autoplay muted loop id='backgroundVideo'>
|
||||
<source src='${customBackground}'/>
|
||||
</video>`;
|
||||
} else {
|
||||
// Local
|
||||
this.setBackground(customBackground, null, 'false');
|
||||
}
|
||||
// Online
|
||||
} else {
|
||||
if (offlineMode === 'true') {
|
||||
return this.doOffline();
|
||||
}
|
||||
|
||||
// First we try and get an image from the API...
|
||||
// background colour
|
||||
const customBackgroundColour = localStorage.getItem('customBackgroundColour');
|
||||
if (customBackgroundColour !== 'Disabled' && customBackgroundColour !== '') {
|
||||
let gradientSettings = '';
|
||||
try {
|
||||
let requestURL;
|
||||
|
||||
switch (localStorage.getItem('backgroundAPI')) {
|
||||
case 'unsplash':
|
||||
requestURL = `${Constants.UNSPLASH_URL}/getImage`;
|
||||
break;
|
||||
// Defaults to Mue
|
||||
default:
|
||||
requestURL = `${Constants.API_URL}/getImage?category=Outdoors`;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch JSON data from requestURL
|
||||
const data = await (await fetch(requestURL)).json();
|
||||
|
||||
// If we hit the rate limit, fallback to local images
|
||||
if (data.statusCode === 429) {
|
||||
this.doOffline();
|
||||
// Otherwise, set the background and credit from remote data
|
||||
} else {
|
||||
this.setBackground(data.file);
|
||||
|
||||
if (localStorage.getItem('backgroundAPI') === 'unsplash') {
|
||||
return this.setCredit(data.photographer, 'unsplash', data.photographer_page);
|
||||
}
|
||||
|
||||
this.setCredit(data.photographer);
|
||||
document.getElementById('camera').textContent = data.camera || 'N/A';
|
||||
document.getElementById('resolution').textContent = data.resolution || 'N/A';
|
||||
}
|
||||
|
||||
if (data.location.replace(/[null]+/g, '') === ' ') {
|
||||
return document.querySelector('#backgroundCredits').style.display = 'none';
|
||||
}
|
||||
|
||||
// Set the location tooltip
|
||||
document.getElementById('location').innerText = `${data.location.replace('null', '')}`;
|
||||
// ..and if that fails we load one locally
|
||||
gradientSettings = JSON.parse(colour);
|
||||
} catch (e) {
|
||||
this.doOffline();
|
||||
const hexColorRegex = /#[0-9a-fA-F]{6}/s;
|
||||
if (hexColorRegex.exec(colour)) {
|
||||
// Colour use to be simply a hex colour or a NULL value before it was a JSON object. This automatically upgrades the hex colour value to the new standard. (NULL would not trigger an exception)
|
||||
gradientSettings = { "type": "linear", "angle": "180", "gradient": [{ "colour": colour, "stop": 0 }] };
|
||||
localStorage.setItem('customBackgroundColour', JSON.stringify(gradientSettings));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof gradientSettings === 'object' && gradientSettings !== null) {
|
||||
return this.gradientStyleBuilder(gradientSettings);
|
||||
}
|
||||
}
|
||||
|
||||
// custom user background
|
||||
const customBackground = localStorage.getItem('customBackground');
|
||||
if (customBackground !== '') {
|
||||
// video background
|
||||
if (customBackground.includes('.mp4') || customBackground.includes('.webm') || customBackground.includes('.ogg')) {
|
||||
return this.setState({
|
||||
url: customBackground,
|
||||
video: true,
|
||||
photoInfo: {
|
||||
hidden: true
|
||||
}
|
||||
});
|
||||
// normal background
|
||||
} else {
|
||||
return this.setState({
|
||||
url: customBackground,
|
||||
photoInfo: {
|
||||
hidden: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// photo pack
|
||||
const photoPack = JSON.parse(localStorage.getItem('photo_packs'));
|
||||
if (photoPack) {
|
||||
const randomPhoto = photoPack[Math.floor(Math.random() * photoPack.length)];
|
||||
return this.setState({
|
||||
url: randomPhoto.url.default,
|
||||
photoInfo: {
|
||||
credit: randomPhoto.photographer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// API background
|
||||
const backgroundAPI = localStorage.getItem('backgroundAPI');
|
||||
|
||||
let requestURL, data;
|
||||
switch (backgroundAPI) {
|
||||
case 'unsplash':
|
||||
requestURL = `${Constants.UNSPLASH_URL}/getImage`;
|
||||
break;
|
||||
// Defaults to Mue
|
||||
default:
|
||||
requestURL = `${Constants.API_URL}/getImage?category=Outdoors`;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
data = await (await fetch(requestURL)).json();
|
||||
} catch (e) {
|
||||
// if requesting to the API fails, we get an offline image
|
||||
return this.offlineBackground();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
url: data.file,
|
||||
photoInfo: {
|
||||
credit: (backgroundAPI !== 'unsplash') ? data.photographer : data.photographer + ' on Unsplash',
|
||||
location: (data.location.replace(/[null]+/g, '') !== ' ') ? data.location : 'N/A',
|
||||
camera: data.camera || 'N/A',
|
||||
resolution: data.resolution || 'N/A'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Hide the credit
|
||||
if (localStorage.getItem('background') === 'false') {
|
||||
return document.querySelector('.photoInformation').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const backgroundColour = localStorage.getItem('customBackgroundColour');
|
||||
if (backgroundColour !== 'Disabled' && backgroundColour !== '') {
|
||||
document.querySelector('.photoInformation').style.display = 'none';
|
||||
}
|
||||
this.getBackground();
|
||||
}
|
||||
|
||||
if (localStorage.getItem('animations') === 'true') {
|
||||
document.querySelector('#backgroundImage').classList.add('fade-in');
|
||||
}
|
||||
|
||||
this.determineMode();
|
||||
// only set once we've got the info
|
||||
componentDidUpdate() {
|
||||
this.setBackground();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div id='backgroundImage'/>;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div id='backgroundImage'/>
|
||||
<PhotoInformation className={this.props.photoInformationClass} info={this.state.photoInfo}/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +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';
|
||||
@@ -10,22 +11,21 @@ export default function PhotoInformation(props) {
|
||||
|
||||
return (
|
||||
<div className='photoInformation'>
|
||||
<h1 id='photographer'>{language.credit}</h1>
|
||||
<h1>{language.credit} {props.info.credit}</h1>
|
||||
<Info className='photoInformationHover'/>
|
||||
<div className={props.className || 'infoCard'}>
|
||||
<Info className='infoIcon'/>
|
||||
<h1>{language.information}</h1>
|
||||
<hr/>
|
||||
<Location/>
|
||||
<span id='location'/>
|
||||
<span>{props.info.location}</span>
|
||||
<Camera/>
|
||||
<span id='camera'/>
|
||||
<span>{props.info.camera}</span>
|
||||
<Resolution/>
|
||||
<span id='resolution'/>
|
||||
<span>{props.info.resolution}</span>
|
||||
<Photographer/>
|
||||
<span id='photographerCard'/>
|
||||
<span>{props.info.credit.split(' on Unsplash')[0]}</span>
|
||||
</div>
|
||||
<span id='credit' style={{ 'display': 'none' }}></span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
left: 1rem;
|
||||
font-size: calc(10px + 0.1vmin) !important;
|
||||
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||
z-index: 99;
|
||||
|
||||
svg {
|
||||
float: left;
|
||||
|
||||
Reference in New Issue
Block a user