Merge pull request #321 from mue/7.0-blur

Kinda add blur hash thing
This commit is contained in:
Isaac
2022-11-15 19:52:30 +00:00
committed by GitHub
8 changed files with 190 additions and 46 deletions

View File

@@ -21,6 +21,7 @@
"@sentry/react": "^7.17.2",
"embla-carousel-autoplay": "^7.0.3",
"embla-carousel-react": "^7.0.3",
"fast-blurhash": "^1.1.1",
"image-conversion": "^2.1.1",
"react": "^18.2.0",
"react-clock": "3.1.0",

6
pnpm-lock.yaml generated
View File

@@ -17,6 +17,7 @@ specifiers:
embla-carousel-react: ^7.0.3
eslint: ^8.26.0
eslint-config-react-app: ^7.0.1
fast-blurhash: ^1.1.1
husky: ^8.0.1
image-conversion: ^2.1.1
prettier: ^2.7.1
@@ -45,6 +46,7 @@ dependencies:
'@sentry/react': 7.17.2_react@18.2.0
embla-carousel-autoplay: 7.0.3
embla-carousel-react: 7.0.3_react@18.2.0
fast-blurhash: 1.1.1
image-conversion: 2.1.1
react: 18.2.0
react-clock: 3.1.0_biqbaboplfbrettd7655fr4n2y
@@ -3605,6 +3607,10 @@ packages:
resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==}
dev: false
/fast-blurhash/1.1.1:
resolution: {integrity: sha512-U3mTWNRE92S4gKARJBbUmEpuYtnet+1BEeUf1qWrUHs54Db4+0aDqI9LBu7doCueFf2SRlfbR89bj+Cm89uw8A==}
dev: false
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true

View File

@@ -0,0 +1,56 @@
import { useState, memo, useEffect } from 'react';
import { useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import Chip from '@mui/material/Chip';
function ChipSelect({ label, options, name }) {
const [optionsSelected, setoptionsSelected] = useState([]);
const handleChange = (event) => {
const {
target: { value },
} = event;
setoptionsSelected(
typeof value === 'string' ? value.split(',') : value,
);
localStorage.setItem('apiCategories', optionsSelected)
};
console.log(localStorage.getItem('apiCategories'))
return (
<div>
<FormControl>
<InputLabel id="chipSelect-label">{label}</InputLabel>
<Select
labelId="chipSelect-label"
id="chipSelect"
multiple
value={optionsSelected}
onChange={handleChange}
input={<OutlinedInput id="select-multiple-chip" label={label} />}
renderValue={(optionsSelected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{optionsSelected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
>
{options.map((option) => (
<MenuItem key={option.name} value={option.name}>
{option.name.charAt(0).toUpperCase() + option.name.slice(1)} ({option.count})
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
export default memo(ChipSelect);

View File

@@ -5,6 +5,7 @@ import { MdSource, MdOutlineKeyboardArrowRight, MdOutlineAutoAwesome } from 'rea
import Header from '../../Header';
import Checkbox from '../../Checkbox';
import ChipSelect from '../../ChipSelect';
import Dropdown from '../../Dropdown';
import Slider from '../../Slider';
import Radio from '../../Radio';
@@ -125,16 +126,25 @@ export default class BackgroundSettings extends PureComponent {
</Dropdown>
</>
) : (
<Dropdown
label={variables.getMessage('modals.main.settings.sections.background.category')}
name="apiCategories"
>
{this.state.backgroundCategories.map((category) => (
<MenuItem value={category.name} key={category.name}>
{category.name.charAt(0).toUpperCase() + category.name.slice(1)} ({category.count})
</MenuItem>
))}
</Dropdown>
<>
{/*<Dropdown
label={variables.getMessage('modals.main.settings.sections.background.category')}
name="apiCategories"
>
{this.state.backgroundCategories.map((category) => (
<MenuItem value={category.name} key={category.name}>
{category.name.charAt(0).toUpperCase() + category.name.slice(1)} (
{category.count})
</MenuItem>
))}
</Dropdown>
<ChipSelect
label={variables.getMessage('modals.main.settings.sections.background.category')}
options={this.state.backgroundCategories}
name="apiCategories"
></ChipSelect>
*/}
</>
)}
<Dropdown
label={variables.getMessage(

View File

@@ -14,6 +14,7 @@ import {
} from 'modules/helpers/background/widget';
import './scss/index.scss';
import { decodeBlurHash } from 'fast-blurhash';
export default class Background extends PureComponent {
constructor() {
@@ -32,7 +33,7 @@ export default class Background extends PureComponent {
};
}
setBackground() {
async setBackground() {
const backgroundImage = document.getElementById('backgroundImage');
if (this.state.url !== '') {
@@ -53,31 +54,60 @@ export default class Background extends PureComponent {
return (backgroundImage.style.background = `url(${url})`);
}
// firstly we set the background as hidden and make sure there is no background set currently
backgroundImage.classList.add('backgroundPreload');
// // firstly we set the background as hidden and make sure there is no background set currently
// backgroundImage.classList.add('backgroundPreload');
backgroundImage.style.background = null;
// same with photo information if not using custom background
photoInformation?.classList.add('backgroundPreload');
// // same with photo information if not using custom background
// photoInformation?.classList.add('backgroundPreload');
// preloader for background transition, required, so it loads in nice
const preloader = document.createElement('img');
preloader.src = url;
// // preloader for background transition, required, so it loads in nice
// const preloader = document.createElement('img');
// preloader.src = url;
// once image has loaded, add the fade-in transition
preloader.addEventListener('load', () => {
backgroundImage.classList.remove('backgroundPreload');
// // once image has loaded, add the fade-in transition
// preloader.addEventListener('load', () => {
// backgroundImage.classList.remove('backgroundPreload');
// backgroundImage.classList.add('fade-in');
// backgroundImage.style.background = `url(${url})`;
// // remove the preloader element we created earlier
// preloader.remove();
// if (photoInformation) {
// photoInformation.classList.remove('backgroundPreload');
// photoInformation.classList.add('fade-in');
// }
// });
if (this.state.photoInfo.blur_hash) {
backgroundImage.style.backgroundColor = this.state.photoInfo.colour;
backgroundImage.classList.add('fade-in');
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(32, 32);
imageData.data.set(decodeBlurHash(this.state.photoInfo.blur_hash, 32, 32));
ctx.putImageData(imageData, 0, 0);
// let blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
// const blobUrl = URL.createObjectURL(blob);
backgroundImage.style.backgroundImage = `url(${canvas.toDataURL()})`;
// backgroundImage.style.backgroundImage = `url(${blobUrl})`;
}
backgroundImage.style.background = `url(${url})`;
// remove the preloader element we created earlier
preloader.remove();
if (photoInformation) {
photoInformation.classList.remove('backgroundPreload');
photoInformation.classList.add('fade-in');
}
});
// const img = new Image();
// img.fetchPriority = 'high';
// img.decoding = 'async';
// img.src = url;
// await img.decode();
// this.state.photoInfo.width = img.width;
// this.state.photoInfo.height = img.height;
const blobUrl = URL.createObjectURL(await (await fetch(url)).blob()); // TODO: revoke this later
backgroundImage.classList.add('backgroundTransform');
backgroundImage.style.backgroundImage = `url(${blobUrl})`;
// img.remove();
// URL.revokeObjectURL(blobUrl);
} else {
// custom colour
backgroundImage.setAttribute('style', this.state.style);
@@ -113,6 +143,14 @@ export default class Background extends PureComponent {
return setFavourited(favourited);
}
let apiCategories;
try {
let apiCategories = JSON.parse(localStorage.getItem('apiCategories'))
} catch (error) {
let apiCategories = localStorage.getItem('apiCategories')
}
const type = localStorage.getItem('backgroundType');
switch (type) {
case 'api':
@@ -122,20 +160,19 @@ export default class Background extends PureComponent {
// API background
const backgroundAPI = localStorage.getItem('backgroundAPI');
const apiCategories = JSON.parse(localStorage.getItem('apiCategories'));
const apiQuality = localStorage.getItem('apiQuality');
let requestURL, data;
switch (backgroundAPI) {
case 'unsplash':
requestURL = `${variables.constants.API_URL}/images/unsplash?category=${apiCategories}&quality=${apiQuality}`;
requestURL = `${variables.constants.API_URL}/images/unsplash?categories=${apiCategories}&quality=${apiQuality}`;
break;
case 'pexels':
requestURL = `${variables.constants.API_URL}/images/pexels?quality=${apiQuality}`;
break;
// Defaults to Mue
default:
requestURL = `${variables.constants.API_URL}/images/random?category=${apiCategories}&quality=${apiQuality}`;
requestURL = `${variables.constants.API_URL}/images/random?categories=${apiCategories}&quality=${apiQuality}`;
break;
}
@@ -172,6 +209,9 @@ export default class Background extends PureComponent {
downloads: data.downloads || null,
likes: data.likes || null,
description: data.description || null,
colour: data.colour,
blur_hash: data.blur_hash,
pun: data.pun || null,
},
};

View File

@@ -97,15 +97,15 @@ function PhotoInformation({ info, url, api }) {
const ddgProxy = localStorage.getItem('ddgProxy') === 'true';
// get resolution
const img = new Image();
img.onload = (event) => {
setWidth(event.target.width);
setHeight(event.target.height);
};
img.src =
ddgProxy && !info.offline && !url.startsWith('data:')
? variables.constants.DDG_IMAGE_PROXY + url
: url;
// const img = new Image();
// img.onload = (event) => {
// setWidth(event.target.width);
// setHeight(event.target.height);
// };
// img.src =
// ddgProxy && !info.offline && !url.startsWith('data:')
// ? variables.constants.DDG_IMAGE_PROXY + url
// : url;
// info is still there because we want the favourite button to work
if (localStorage.getItem('photoInformation') === 'false') {
@@ -132,7 +132,6 @@ function PhotoInformation({ info, url, api }) {
return null;
}
const zoom = 12;
const tile = variables.constants.API_URL + `/map?latitude=${info.latitude}&longitude=${info.longitude}`;
showingPhotoMap = true;
@@ -318,4 +317,4 @@ function PhotoInformation({ info, url, api }) {
);
}
export default memo(PhotoInformation);
export default memo(PhotoInformation);

View File

@@ -23,7 +23,7 @@
}
.concept-stats {
display: flex;
padding-top: 5px;
padding-top: 5px;
gap: 20px;
@include themed() {
color: t($subColor);
@@ -61,6 +61,23 @@
}
}
.mapOn {
width: 100%;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
img {
flex-shrink: 0;
min-width: 100%;
min-height: 100%;
}
svg {
display: none;
}
}
.photoInformation-legacy {
font-size: 1.36em;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
@@ -293,7 +310,7 @@
}
.photoInformation-content {
line-height: 2em;
line-height: 2em;
margin-right: 3px;
display: flex;
flex-flow: column;

View File

@@ -10,6 +10,21 @@
zoom: 100% !important;
}
.backgroundTransform {
@include animation('fadein2 2s');
transition: background 2s;
}
@include keyframes(fadein2) {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.backgroundPreload {
opacity: 0;
}