Compare commits

..

15 commits

Author SHA1 Message Date
FloatingGhost
e7a558a533 Merge branch 'develop' into stable 2023-08-05 14:17:53 +01:00
FloatingGhost
5106fcedd6 Merge branch 'develop' into stable 2023-08-05 13:29:55 +01:00
FloatingGhost
1db322bae6 Merge branch 'develop' into stable 2023-08-05 13:29:26 +01:00
FloatingGhost
e530c2b462 Merge branch 'develop' into stable 2023-05-23 14:10:31 +01:00
FloatingGhost
9aa64d82c9 Merge branch 'develop' into stable 2023-04-14 18:10:41 +01:00
FloatingGhost
85abc62213 Merge branch 'develop' into stable 2023-03-11 17:27:22 +00:00
FloatingGhost
8569b5946e Merge branch 'develop' into stable 2023-02-11 10:50:04 +00:00
FloatingGhost
9c9b4cc07c Merge branch 'develop' into stable 2022-12-10 14:52:00 +00:00
floatingghost
2c9b73646c Merge pull request 'hotfix: mfm mysteries' (#215) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/215
2022-11-15 16:01:07 +00:00
floatingghost
80a519d7e4 Merge pull request 'hotfix: translation' (#207) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/207
2022-11-12 19:08:20 +00:00
floatingghost
975f04bf5a Merge pull request '2022.11 stable release' (#202) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/202
2022-11-12 15:33:57 +00:00
floatingghost
c8c8d40827 Merge pull request '2022.10 stable' (#177) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/177
2022-10-08 11:13:01 +00:00
floatingghost
d7499a1f91 Merge pull request '2022.09 stable' (#160) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/160
2022-09-10 14:39:13 +00:00
floatingghost
5972d89117 Merge pull request 'stable release' (#130) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/130
2022-08-12 15:26:51 +00:00
floatingghost
d03872d598 Merge pull request 'port MFM link into stable docs' (#38) from develop into stable
Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/38
2022-07-15 13:02:06 +00:00
4 changed files with 48 additions and 117 deletions

View file

@ -13,7 +13,6 @@ const StillImage = {
return {
stopGifs: this.$store.getters.mergedConfig.stopGifs || window.matchMedia('(prefers-reduced-motion: reduce)').matches,
isAnimated: false,
imageTypeLabel: ''
}
},
computed: {
@ -40,22 +39,27 @@ const StillImage = {
this.imageLoadError && this.imageLoadError()
},
detectAnimation (image) {
// If there are no file extensions, the mimetype isn't set, and no mediaproxy is available, we can't figure out
// the mimetype of the image.
const hasFileExtension = this.src.split('/').pop().includes('.') // TODO: Better check?
const mediaProxyAvailable = this.$store.state.instance.mediaProxyAvailable
// harmless CORS errors without-- clean console with
if (!mediaProxyAvailable) {
if (!hasFileExtension && this.mimetype === undefined && !mediaProxyAvailable) {
// It's a bit aggressive to assume all images we can't find the mimetype of is animated, but necessary for
// people in need of reduced motion accessibility. As such, we'll consider those images animated if the user
// agent is set to prefer reduced motion. Otherwise, it'll just be used as an early exit.
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// Only for no media-proxy for now, since we're capturing gif, webp, and apng (are there others?)
// Since the canvas and images are not pixel-perfect matching (due to scaling),
// It makes the images jiggle on hover, which is not ideal for accessibility, methinks
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches)
this.isAnimated = true
}
return
}
if (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) {
this.isAnimated = true
return
}
// harmless CORS errors without-- clean console with
if (!mediaProxyAvailable) return
// Animated JPEGs?
if (!(this.src.endsWith('.webp') || this.src.endsWith('.png'))) return
// Browser Cache should ensure image doesn't get loaded twice if cache exists
fetch(image.src, {
referrerPolicy: 'same-origin'
@ -64,20 +68,12 @@ const StillImage = {
// We don't need to read the whole file so only call it once
data.body.getReader().read()
.then(reader => {
// Ordered from least to most intensive
if (this.isGIF(reader.value)) {
if (this.src.endsWith('.webp') && this.isAnimatedWEBP(reader.value)) {
this.isAnimated = true
this.setLabel('GIF')
return
}
if (this.isAnimatedWEBP(reader.value)) {
if (this.src.endsWith('.png') && this.isAnimatedPNG(reader.value)) {
this.isAnimated = true
this.setLabel('WEBP')
return
}
if (this.isAnimatedPNG(reader.value)) {
this.isAnimated = true
this.setLabel('APNG')
}
})
})
@ -85,23 +81,6 @@ const StillImage = {
// this.imageLoadError && this.imageLoadError()
})
},
setLabel (name) {
this.imageTypeLabel = name;
},
isGIF (data) {
// I am a perfectly sane individual
//
// GIF HEADER CHUNK
// === START HEADER ===
// 47 49 46 38 ("GIF8")
const gifHeader = [0x47, 0x49, 0x46];
for (let i = 0; i < 3; i++) {
if (data[i] !== gifHeader[i]) {
return false;
}
}
return true
},
isAnimatedWEBP (data) {
/**
* WEBP HEADER CHUNK
@ -135,55 +114,16 @@ const StillImage = {
const idatPos = str.indexOf('IDAT')
return (str.substring(0, idatPos > 0 ? idatPos : 0).indexOf('acTL') > 0)
},
drawThumbnail() {
const canvas = this.$refs.canvas;
if (!canvas) return;
const context = canvas.getContext('2d');
const image = this.$refs.src;
const parentElement = canvas.parentElement;
// Draw the quick, unscaled version first
context.drawImage(image, 0, 0, parentElement.clientWidth, parentElement.clientHeight);
// Use requestAnimationFrame to schedule the scaling to the next frame
requestAnimationFrame(() => {
// Compute scaling ratio between the natural dimensions of the image and its display dimensions
const scalingRatioWidth = parentElement.clientWidth / image.naturalWidth;
const scalingRatioHeight = parentElement.clientHeight / image.naturalHeight;
// Adjust for high-DPI displays
const ratio = window.devicePixelRatio || 1;
canvas.width = image.naturalWidth * scalingRatioWidth * ratio;
canvas.height = image.naturalHeight * scalingRatioHeight * ratio;
canvas.style.width = `${parentElement.clientWidth}px`;
canvas.style.height = `${parentElement.clientHeight}px`;
context.scale(ratio, ratio);
// Maintain the aspect ratio of the image
const imgAspectRatio = image.naturalWidth / image.naturalHeight;
const canvasAspectRatio = parentElement.clientWidth / parentElement.clientHeight;
let drawWidth, drawHeight;
if (imgAspectRatio > canvasAspectRatio) {
drawWidth = parentElement.clientWidth;
drawHeight = parentElement.clientWidth / imgAspectRatio;
} else {
drawHeight = parentElement.clientHeight;
drawWidth = parentElement.clientHeight * imgAspectRatio;
}
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the previous unscaled image
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = 'high';
// Draw the good one for realsies
const dx = (parentElement.clientWidth - drawWidth) / 2;
const dy = (parentElement.clientHeight - drawHeight) / 2;
context.drawImage(image, dx, dy, drawWidth, drawHeight);
});
}
drawThumbnail () {
const canvas = this.$refs.canvas
if (!this.$refs.canvas) return
const image = this.$refs.src
const width = image.naturalWidth
const height = image.naturalHeight
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
}
},
updated () {
// On computed animated change

View file

@ -1,15 +1,9 @@
<template>
<div
ref="still-image"
class="still-image"
:class="{ animated: animated }"
:style="style"
>
<div
v-if="animated && imageTypeLabel"
class="image-type-label">
{{ imageTypeLabel }}
</div>
<canvas
v-if="animated"
ref="canvas"
@ -63,26 +57,30 @@
}
}
.image-type-label {
position: absolute;
top: 0.25em;
left: 0.25em;
line-height: 1;
font-size: 0.6em;
background: rgba(127, 127, 127, 0.5);
color: #fff;
padding: 2px 4px;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
z-index: 2;
visibility: var(--_still-image-label-visibility, visible);
}
&.animated {
&::before {
zoom: var(--_still_image-label-scale, 1);
content: 'gif';
position: absolute;
line-height: 1;
font-size: 0.7em;
top: 0.5em;
left: 0.5em;
background: rgba(127, 127, 127, 0.5);
color: #fff;
display: block;
padding: 2px 4px;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
z-index: 2;
visibility: var(--_still-image-label-visibility, visible);
}
&:hover canvas {
display: none;
}
&:hover .image-type-label {
&:hover::before {
visibility: var(--_still-image-label-visibility, hidden);
}

View file

@ -33,7 +33,7 @@
--_avatarShadowBox: var(--avatarStatusShadow);
--_avatarShadowFilter: var(--avatarStatusShadowFilter);
--_avatarShadowInset: var(--avatarStatusShadowInset);
// --_still-image-label-visibility: hidden;
--_still-image-label-visibility: hidden;
display: inline-block;
position: relative;

View file

@ -37,18 +37,11 @@ const recentEmojis = {
getters: {
recentEmojis: (state, getters, rootState) => state.emojis.reduce((objects, displayText) => {
let comparator = emoji => emoji.displayText === displayText
let emojiObject = rootState.instance.emoji.find(comparator)
const allEmojis = rootState.instance.emoji.concat(rootState.instance.customEmoji)
let emojiObject = allEmojis.find(emoji => emoji.displayText === displayText)
if (emojiObject !== undefined) {
objects.push(emojiObject)
} else {
emojiObject = rootState.instance.customEmoji.find(comparator)
if (emojiObject !== undefined) {
objects.push(emojiObject)
}
}
return objects
}, []),
},