Refactor Wagtail Images to srcset
This commit is contained in:
parent
d69b32cba1
commit
0452389951
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<wagtail-image
|
||||
:src="value.src"
|
||||
:srcset="value.srcset"
|
||||
alt=""
|
||||
:original-height="value.height"
|
||||
:original-width="value.width"
|
||||
|
|
@ -17,7 +18,7 @@ export default {
|
|||
components: { WagtailImage },
|
||||
methods: {
|
||||
openFullscreen() {
|
||||
this.$store.dispatch('showFullscreenImage', this.value.url);
|
||||
this.$store.dispatch('showFullscreenImage', this.value.src);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default {
|
|||
'-/resize/800/ 800w,'
|
||||
);
|
||||
}
|
||||
return '';
|
||||
return this.value.url;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,12 @@
|
|||
<wagtail-image
|
||||
class="module__hero-image"
|
||||
:src="module.heroImage.src"
|
||||
:srcset="module.heroImage.srcset"
|
||||
:original-width="module.heroImage.width"
|
||||
:original-height="module.heroImage.height"
|
||||
></wagtail-image>
|
||||
|
||||
|
||||
<h5
|
||||
class="module__hero-source"
|
||||
v-if="module.heroSource"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<wagtail-image
|
||||
class="module-teaser__image"
|
||||
:src="heroImage.src"
|
||||
:srcset="heroImage.srcset"
|
||||
:original-height="heroImage.height"
|
||||
:original-width="heroImage.width"
|
||||
></wagtail-image>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<div class="wagtail-image">
|
||||
<img
|
||||
:src="modifiedUrl"
|
||||
:src="props.src"
|
||||
:srcset="props.srcset"
|
||||
:alt="alt"
|
||||
:class="['wagtail-image__image', { loaded: loaded }]"
|
||||
:sizes="computedSizes"
|
||||
loading="eager"
|
||||
v-show="loaded"
|
||||
ref="imgElement"
|
||||
|
|
@ -16,15 +18,15 @@
|
|||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
src: String,
|
||||
alt: String(''),
|
||||
originalWidth: Number,
|
||||
originalHeight: Number,
|
||||
srcset: String,
|
||||
});
|
||||
|
||||
const imgElement = ref(null);
|
||||
|
|
@ -32,19 +34,6 @@ const width = ref(0);
|
|||
const height = ref(0);
|
||||
const loaded = ref(false);
|
||||
|
||||
const modifiedUrl = computed(() => {
|
||||
const density = isHighDensity() ? 2 : 1;
|
||||
|
||||
if (width.value) {
|
||||
return props.src.replace('original', 'width-' + width.value * density);
|
||||
}
|
||||
if (height.value) {
|
||||
return props.src.replace('original', 'height-' + height.value * density);
|
||||
}
|
||||
|
||||
return props.src;
|
||||
});
|
||||
|
||||
const updateDimensions = () => {
|
||||
if (imgElement.value && imgElement.value.parentElement) {
|
||||
const { clientWidth, clientHeight } = imgElement.value.parentElement;
|
||||
|
|
@ -57,6 +46,23 @@ const handleLoad = () => {
|
|||
loaded.value = true; // Set loaded to true when the image loads
|
||||
};
|
||||
|
||||
const computedSizes = computed(() => {
|
||||
// the default set of image sizes is [160px, 320px, 800px, 1600px]
|
||||
let size;
|
||||
|
||||
if (width.value <= 300) {
|
||||
size = '160px';
|
||||
} else if (300 < width.value && width.value <= 600) {
|
||||
size = '320px';
|
||||
} else if (600 < width.value && width.value <= 1200) {
|
||||
size = '800px';
|
||||
} else {
|
||||
size = '100vw';
|
||||
}
|
||||
console.log(size, width.value);
|
||||
return size;
|
||||
});
|
||||
|
||||
const placeholderStyle = computed(() => {
|
||||
const styles = {
|
||||
width: '100%',
|
||||
|
|
@ -77,20 +83,15 @@ const placeholderStyle = computed(() => {
|
|||
return styles;
|
||||
});
|
||||
|
||||
const isHighDensity = () => {
|
||||
return (
|
||||
(window.matchMedia &&
|
||||
(window.matchMedia(
|
||||
'only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)'
|
||||
).matches ||
|
||||
window.matchMedia(
|
||||
'only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)'
|
||||
).matches)) ||
|
||||
(window.devicePixelRatio && window.devicePixelRatio > 1.3)
|
||||
);
|
||||
};
|
||||
onMounted(() => {
|
||||
console.log('src set', props.srcset);
|
||||
updateDimensions();
|
||||
window.addEventListener('resize', updateDimensions);
|
||||
});
|
||||
|
||||
onMounted(updateDimensions);
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateDimensions);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ fragment WagtailImageParts on WagtailImageNode {
|
|||
width
|
||||
height
|
||||
title
|
||||
srcset
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ from graphene.types import Scalar
|
|||
from graphene_django.converter import convert_django_field
|
||||
from graphql_relay import to_global_id
|
||||
from wagtail.fields import StreamField
|
||||
from wagtail.documents.models import Document
|
||||
from wagtail.images.models import Image
|
||||
|
||||
from assignments.models import Assignment
|
||||
|
|
@ -14,6 +13,7 @@ from basicknowledge.models import BasicKnowledge
|
|||
from books.models import CustomDocument
|
||||
from surveys.models import Survey
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -43,17 +43,19 @@ def get_document_json(document_id):
|
|||
|
||||
|
||||
def get_wagtail_image_dict(image_id: int) -> dict | None:
|
||||
""""""
|
||||
from books.schema.nodes import get_srcset, get_src
|
||||
|
||||
try:
|
||||
image = Image.objects.get(id=image_id)
|
||||
value = {
|
||||
'value': image_id,
|
||||
'id': image.id,
|
||||
'src': generate_image_url(image, 'original'),
|
||||
'src': get_src(image),
|
||||
'alt': image.title,
|
||||
'title': image.title,
|
||||
'width': image.width,
|
||||
'height': image.height,
|
||||
'srcset': get_srcset(image),
|
||||
}
|
||||
return value
|
||||
except Image.DoesNotExist:
|
||||
|
|
|
|||
|
|
@ -7,8 +7,20 @@ from wagtail.images.models import Image
|
|||
from wagtail.images.views.serve import generate_image_url
|
||||
|
||||
|
||||
class WagtailImageNode(DjangoObjectType):
|
||||
def get_srcset(image: Image) -> str:
|
||||
return (
|
||||
f"{generate_image_url(image, 'width-160')} 160w, "
|
||||
f"{generate_image_url(image, 'width-320')} 320w, "
|
||||
f"{generate_image_url(image, 'width-800')} 1280w, "
|
||||
f"{generate_image_url(image, 'width-1600')} 2560w"
|
||||
)
|
||||
|
||||
|
||||
def get_src(image: Image) -> str:
|
||||
return generate_image_url(image, f'width-{min(3840, image.width)}')
|
||||
|
||||
|
||||
class WagtailImageNode(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = [
|
||||
|
|
@ -20,10 +32,13 @@ class WagtailImageNode(DjangoObjectType):
|
|||
|
||||
src = graphene.String()
|
||||
alt = graphene.String()
|
||||
|
||||
srcset = graphene.String()
|
||||
|
||||
def resolve_src(self, info):
|
||||
return generate_image_url(self, 'original')
|
||||
return get_src(self)
|
||||
|
||||
def resolve_alt(self, info):
|
||||
return ""
|
||||
return self.title
|
||||
|
||||
def resolve_srcset(self, info):
|
||||
return get_srcset(self)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ from django.views.generic import RedirectView
|
|||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
#from wagtail.images import urls as wagtailimages_urls
|
||||
from core.views import CustomImageServeView
|
||||
from wagtail.images.views.serve import ServeView
|
||||
|
||||
from wagtailautocomplete.urls.admin import urlpatterns as autocomplete_admin_urls
|
||||
|
||||
|
|
@ -19,14 +18,13 @@ urlpatterns = [
|
|||
re_path(r"^guru/", admin.site.urls),
|
||||
re_path(r"^statistics/", include("statistics.urls", namespace="statistics")),
|
||||
# wagtail
|
||||
re_path(r'^api/images/([^/]*)/(\d*)/([^/]*)/[^/]*$', CustomImageServeView.as_view(action='redirect'), name='wagtailimages_serve'),
|
||||
re_path(r'^api/images/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(action='redirect'), name='wagtailimages_serve'),
|
||||
|
||||
re_path(r"^cms/autocomplete/", include(autocomplete_admin_urls)),
|
||||
re_path(r"^cms/pages/(\d+)/$", override_wagtailadmin_explore_default_ordering),
|
||||
re_path(r"^cms/", include(wagtailadmin_urls)),
|
||||
re_path(r"^documents/", include(wagtaildocs_urls)),
|
||||
|
||||
#re_path(r"^images/", include(wagtailimages_urls)),
|
||||
|
||||
# graphql backend
|
||||
re_path(r"^api/", include("api.urls", namespace="api")),
|
||||
|
|
|
|||
|
|
@ -1,29 +1,15 @@
|
|||
import imghdr
|
||||
from wsgiref.util import FileWrapper
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponse, StreamingHttpResponse
|
||||
from django.http import HttpResponse
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.shortcuts import render
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.cache import cache_control
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from graphene_django.views import GraphQLView
|
||||
from graphql import get_operation_ast, parse
|
||||
from sentry_sdk.api import start_transaction
|
||||
from wagtail.admin.views.pages import listing
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.exceptions import InvalidFilterSpecError
|
||||
from wagtail.images.models import Image
|
||||
from wagtail.images.models import SourceImageIOError
|
||||
from wagtail.images.utils import verify_signature
|
||||
from wagtail.images.views.serve import ServeView
|
||||
|
||||
from core.logger import get_logger
|
||||
|
||||
|
|
@ -85,36 +71,3 @@ def override_wagtailadmin_explore_default_ordering(request, parent_page_id):
|
|||
|
||||
return listing.IndexView.as_view()(request, parent_page_id)
|
||||
|
||||
class CustomImageServeView(ServeView):
|
||||
''' Taken from wagtail.images.views.serve.ServeView the only change is that we use original for the filter_spec_for_signature'''
|
||||
model = get_image_model()
|
||||
action = "serve"
|
||||
key = None
|
||||
|
||||
@method_decorator(cache_control(max_age=3600, public=True))
|
||||
def get(self, request, signature, image_id, filter_spec, filename=None):
|
||||
# This variable is the only change to the wagtail implementation
|
||||
filter_spec_for_signature = 'original'
|
||||
|
||||
if not verify_signature(
|
||||
signature.encode(), image_id, filter_spec_for_signature, key=self.key
|
||||
):
|
||||
raise PermissionDenied
|
||||
|
||||
image = get_object_or_404(self.model, id=image_id)
|
||||
|
||||
# Get/generate the rendition
|
||||
try:
|
||||
rendition = image.get_rendition(filter_spec)
|
||||
except SourceImageIOError:
|
||||
return HttpResponse(
|
||||
"Source image file not found", content_type="text/plain", status=410
|
||||
)
|
||||
except InvalidFilterSpecError:
|
||||
return HttpResponse(
|
||||
"Invalid filter spec: " + filter_spec,
|
||||
content_type="text/plain",
|
||||
status=400,
|
||||
)
|
||||
|
||||
return getattr(self, self.action)(rendition)
|
||||
|
|
|
|||
Loading…
Reference in New Issue