Refactor Wagtail Images to srcset
This commit is contained in:
parent
d69b32cba1
commit
0452389951
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<wagtail-image
|
<wagtail-image
|
||||||
:src="value.src"
|
:src="value.src"
|
||||||
|
:srcset="value.srcset"
|
||||||
alt=""
|
alt=""
|
||||||
:original-height="value.height"
|
:original-height="value.height"
|
||||||
:original-width="value.width"
|
:original-width="value.width"
|
||||||
|
|
@ -17,7 +18,7 @@ export default {
|
||||||
components: { WagtailImage },
|
components: { WagtailImage },
|
||||||
methods: {
|
methods: {
|
||||||
openFullscreen() {
|
openFullscreen() {
|
||||||
this.$store.dispatch('showFullscreenImage', this.value.url);
|
this.$store.dispatch('showFullscreenImage', this.value.src);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export default {
|
||||||
'-/resize/800/ 800w,'
|
'-/resize/800/ 800w,'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return '';
|
return this.value.url;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,12 @@
|
||||||
<wagtail-image
|
<wagtail-image
|
||||||
class="module__hero-image"
|
class="module__hero-image"
|
||||||
:src="module.heroImage.src"
|
:src="module.heroImage.src"
|
||||||
|
:srcset="module.heroImage.srcset"
|
||||||
:original-width="module.heroImage.width"
|
:original-width="module.heroImage.width"
|
||||||
:original-height="module.heroImage.height"
|
:original-height="module.heroImage.height"
|
||||||
></wagtail-image>
|
></wagtail-image>
|
||||||
|
|
||||||
|
|
||||||
<h5
|
<h5
|
||||||
class="module__hero-source"
|
class="module__hero-source"
|
||||||
v-if="module.heroSource"
|
v-if="module.heroSource"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<wagtail-image
|
<wagtail-image
|
||||||
class="module-teaser__image"
|
class="module-teaser__image"
|
||||||
:src="heroImage.src"
|
:src="heroImage.src"
|
||||||
|
:srcset="heroImage.srcset"
|
||||||
:original-height="heroImage.height"
|
:original-height="heroImage.height"
|
||||||
:original-width="heroImage.width"
|
:original-width="heroImage.width"
|
||||||
></wagtail-image>
|
></wagtail-image>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wagtail-image">
|
<div class="wagtail-image">
|
||||||
<img
|
<img
|
||||||
:src="modifiedUrl"
|
:src="props.src"
|
||||||
|
:srcset="props.srcset"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
:class="['wagtail-image__image', { loaded: loaded }]"
|
:class="['wagtail-image__image', { loaded: loaded }]"
|
||||||
|
:sizes="computedSizes"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
v-show="loaded"
|
v-show="loaded"
|
||||||
ref="imgElement"
|
ref="imgElement"
|
||||||
|
|
@ -16,15 +18,15 @@
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
src: String,
|
src: String,
|
||||||
alt: String(''),
|
alt: String(''),
|
||||||
originalWidth: Number,
|
originalWidth: Number,
|
||||||
originalHeight: Number,
|
originalHeight: Number,
|
||||||
|
srcset: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
const imgElement = ref(null);
|
const imgElement = ref(null);
|
||||||
|
|
@ -32,19 +34,6 @@ const width = ref(0);
|
||||||
const height = ref(0);
|
const height = ref(0);
|
||||||
const loaded = ref(false);
|
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 = () => {
|
const updateDimensions = () => {
|
||||||
if (imgElement.value && imgElement.value.parentElement) {
|
if (imgElement.value && imgElement.value.parentElement) {
|
||||||
const { clientWidth, clientHeight } = 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
|
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 placeholderStyle = computed(() => {
|
||||||
const styles = {
|
const styles = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|
@ -77,20 +83,15 @@ const placeholderStyle = computed(() => {
|
||||||
return styles;
|
return styles;
|
||||||
});
|
});
|
||||||
|
|
||||||
const isHighDensity = () => {
|
onMounted(() => {
|
||||||
return (
|
console.log('src set', props.srcset);
|
||||||
(window.matchMedia &&
|
updateDimensions();
|
||||||
(window.matchMedia(
|
window.addEventListener('resize', updateDimensions);
|
||||||
'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(updateDimensions);
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', updateDimensions);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,5 @@ fragment WagtailImageParts on WagtailImageNode {
|
||||||
width
|
width
|
||||||
height
|
height
|
||||||
title
|
title
|
||||||
|
srcset
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ from graphene.types import Scalar
|
||||||
from graphene_django.converter import convert_django_field
|
from graphene_django.converter import convert_django_field
|
||||||
from graphql_relay import to_global_id
|
from graphql_relay import to_global_id
|
||||||
from wagtail.fields import StreamField
|
from wagtail.fields import StreamField
|
||||||
from wagtail.documents.models import Document
|
|
||||||
from wagtail.images.models import Image
|
from wagtail.images.models import Image
|
||||||
|
|
||||||
from assignments.models import Assignment
|
from assignments.models import Assignment
|
||||||
|
|
@ -14,6 +13,7 @@ from basicknowledge.models import BasicKnowledge
|
||||||
from books.models import CustomDocument
|
from books.models import CustomDocument
|
||||||
from surveys.models import Survey
|
from surveys.models import Survey
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,17 +43,19 @@ def get_document_json(document_id):
|
||||||
|
|
||||||
|
|
||||||
def get_wagtail_image_dict(image_id: int) -> dict | None:
|
def get_wagtail_image_dict(image_id: int) -> dict | None:
|
||||||
""""""
|
from books.schema.nodes import get_srcset, get_src
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image = Image.objects.get(id=image_id)
|
image = Image.objects.get(id=image_id)
|
||||||
value = {
|
value = {
|
||||||
'value': image_id,
|
'value': image_id,
|
||||||
'id': image.id,
|
'id': image.id,
|
||||||
'src': generate_image_url(image, 'original'),
|
'src': get_src(image),
|
||||||
'alt': image.title,
|
'alt': image.title,
|
||||||
'title': image.title,
|
'title': image.title,
|
||||||
'width': image.width,
|
'width': image.width,
|
||||||
'height': image.height,
|
'height': image.height,
|
||||||
|
'srcset': get_srcset(image),
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
except Image.DoesNotExist:
|
except Image.DoesNotExist:
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,20 @@ from wagtail.images.models import Image
|
||||||
from wagtail.images.views.serve import generate_image_url
|
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:
|
class Meta:
|
||||||
model = Image
|
model = Image
|
||||||
fields = [
|
fields = [
|
||||||
|
|
@ -20,10 +32,13 @@ class WagtailImageNode(DjangoObjectType):
|
||||||
|
|
||||||
src = graphene.String()
|
src = graphene.String()
|
||||||
alt = graphene.String()
|
alt = graphene.String()
|
||||||
|
srcset = graphene.String()
|
||||||
|
|
||||||
def resolve_src(self, info):
|
def resolve_src(self, info):
|
||||||
return generate_image_url(self, 'original')
|
return get_src(self)
|
||||||
|
|
||||||
def resolve_alt(self, info):
|
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.admin import urls as wagtailadmin_urls
|
||||||
from wagtail import urls as wagtail_urls
|
from wagtail import urls as wagtail_urls
|
||||||
from wagtail.documents import urls as wagtaildocs_urls
|
from wagtail.documents import urls as wagtaildocs_urls
|
||||||
#from wagtail.images import urls as wagtailimages_urls
|
from wagtail.images.views.serve import ServeView
|
||||||
from core.views import CustomImageServeView
|
|
||||||
|
|
||||||
from wagtailautocomplete.urls.admin import urlpatterns as autocomplete_admin_urls
|
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"^guru/", admin.site.urls),
|
||||||
re_path(r"^statistics/", include("statistics.urls", namespace="statistics")),
|
re_path(r"^statistics/", include("statistics.urls", namespace="statistics")),
|
||||||
# wagtail
|
# 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/autocomplete/", include(autocomplete_admin_urls)),
|
||||||
re_path(r"^cms/pages/(\d+)/$", override_wagtailadmin_explore_default_ordering),
|
re_path(r"^cms/pages/(\d+)/$", override_wagtailadmin_explore_default_ordering),
|
||||||
re_path(r"^cms/", include(wagtailadmin_urls)),
|
re_path(r"^cms/", include(wagtailadmin_urls)),
|
||||||
re_path(r"^documents/", include(wagtaildocs_urls)),
|
re_path(r"^documents/", include(wagtaildocs_urls)),
|
||||||
|
|
||||||
#re_path(r"^images/", include(wagtailimages_urls)),
|
|
||||||
|
|
||||||
# graphql backend
|
# graphql backend
|
||||||
re_path(r"^api/", include("api.urls", namespace="api")),
|
re_path(r"^api/", include("api.urls", namespace="api")),
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,15 @@
|
||||||
import imghdr
|
|
||||||
from wsgiref.util import FileWrapper
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.http import HttpResponse
|
||||||
from django.http import HttpResponse, StreamingHttpResponse
|
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.shortcuts import render
|
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 django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from graphene_django.views import GraphQLView
|
from graphene_django.views import GraphQLView
|
||||||
from graphql import get_operation_ast, parse
|
from graphql import get_operation_ast, parse
|
||||||
from sentry_sdk.api import start_transaction
|
from sentry_sdk.api import start_transaction
|
||||||
from wagtail.admin.views.pages import listing
|
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
|
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)
|
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