Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/rapidez/models.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'option_value' => Rapidez\Core\Models\OptionValue::class,
'product_image' => Rapidez\Core\Models\ProductImage::class,
'product_image_value' => Rapidez\Core\Models\ProductImageValue::class,
'product_video_value' => Rapidez\Core\Models\ProductVideoValue::class,
'product_link' => Rapidez\Core\Models\ProductLink::class,
'product_view' => Rapidez\Core\Models\ProductView::class,
'product_option' => Rapidez\Core\Models\ProductOption::class,
Expand Down
6 changes: 4 additions & 2 deletions resources/js/components/Product/Images.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useEventListener } from '@vueuse/core'
export default {
data: () => ({
images: config.product.images,
media: config.product.product_gallery,
active: 0,
zoomed: false,
stopKeyUpListener: () => {},
Expand All @@ -22,7 +23,8 @@ export default {
Object.values(window.config.product.super_attributes).filter((attribute) => attribute.update_image).length
) {
self.images = simpleProduct.images
self.active = Math.min(self.active, self.images.length - 1)
self.media = simpleProduct.product_gallery
self.active = Math.min(self.active, self.media.length - 1)
}
})
})
Expand All @@ -44,7 +46,7 @@ export default {
if (e.key == 'ArrowLeft' && this.active > 0) {
// left
this.active--
} else if (e.key == 'ArrowRight' && this.active < this.images.length - 1) {
} else if (e.key == 'ArrowRight' && this.active < this.media.length - 1) {
// right
this.active++
} else if (e.key == 'Escape') {
Expand Down
80 changes: 56 additions & 24 deletions resources/views/product/partials/images.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
@include('rapidez::wishlist.button')
</div>
@endif
@if (count($product->images))
@if (count($product->product_gallery))
<div class="absolute inset-0 flex h-[440px] items-center justify-center rounded border p-5">
<img
class="m-auto max-h-[400px] w-full object-contain"
src="/storage/{{ config('rapidez.store') }}/resizes/400/magento/catalog/product{{ $product->images[0] }}.webp"
@if ($product->product_gallery[0]['media_type'] === 'external-video')
src="{{ config('rapidez.media_url').'/catalog/product'.$product->product_gallery[0]['image'] }}"
@else
src="/storage/{{ config('rapidez.store') }}/resizes/400/magento/catalog/product{{ $product->product_gallery[0]['image'] }}.webp"
@endif
alt="{{ $product->name }}"
width="400"
height="400"
Expand All @@ -17,54 +21,82 @@ class="m-auto max-h-[400px] w-full object-contain"
@endif

<images v-cloak>
<div slot-scope="{ images, active, zoomed, toggleZoom, change }">
<div class="relative" v-if="images.length">
<div slot-scope="{ media, active, zoomed, toggleZoom, change }">
<div class="relative" v-if="media.length">
<a
:href="config.media_url + '/catalog/product' + images[active]"
:href="config.media_url + '/catalog/product' + media[active].image"
class="flex items-center justify-center"
:class="zoomed ? 'fixed inset-0 bg-white !h-full {{ config('rapidez.frontend.z-indexes.lightbox') }} cursor-[zoom-out]' : 'border rounded p-5 h-[440px]'"
v-on:click.prevent="toggleZoom"
>
<img
v-if="!zoomed"
:src="'/storage/{{ config('rapidez.store') }}/resizes/400/magento/catalog/product' + images[active] + '.webp'"
alt="{{ $product->name }}"
class="object-contain w-full m-auto max-h-[400px]"
width="400"
height="400"
/>
<img
v-else
:src="config.media_url + '/catalog/product' + images[active]"
alt="{{ $product->name }}"
class="max-h-full max-w-full"
<template v-if="media[active].media_type === 'image'">
<img
v-if="!zoomed"
:src="'/storage/{{ config('rapidez.store') }}/resizes/400/magento/catalog/product' + media[active].image + '.webp'"
alt="{{ $product->name }}"
class="object-contain w-full m-auto max-h-[400px]"
width="400"
height="400"
/>
<img
v-else
:src="config.media_url + '/catalog/product' + media[active].image"
alt="{{ $product->name }}"
class="max-h-full max-w-full"
loading="lazy"
/>
</template>
<iframe
v-else-if="media[active].media_type === 'external-video'"
:class="{
'h-[calc(100%-200px)] w-[calc(100%-200px)]': zoomed,
'h-full w-full': !zoomed
}"
:src="media[active].video_url"
allow="autoplay"
width="100%"
height="100%"
frameborder="0"
loading="lazy"
/>
>
</iframe>
</a>
<button class="{{ config('rapidez.frontend.z-indexes.lightbox') }} top-1/2 left-3 -translate-y-1/2" :class="zoomed ? 'fixed' : 'absolute'" v-if="active" v-on:click="change(active-1)" aria-label="@lang('Prev')">
<x-heroicon-o-chevron-left class="h-8 w-8 text-gray-900" />
</button>
<button class="{{ config('rapidez.frontend.z-indexes.lightbox') }} top-1/2 right-3 -translate-y-1/2" :class="zoomed ? 'fixed' : 'absolute'" v-if="active != images.length-1" v-on:click="change(active+1)" aria-label="@lang('Next')">
<button class="{{ config('rapidez.frontend.z-indexes.lightbox') }} top-1/2 right-3 -translate-y-1/2" :class="zoomed ? 'fixed' : 'absolute'" v-if="active != media.length-1" v-on:click="change(active+1)" aria-label="@lang('Next')">
<x-heroicon-o-chevron-right class="h-8 w-8 text-gray-900" />
</button>
</div>
<x-rapidez::no-image v-else class="h-96 rounded" />

<div v-if="images.length > 1" class="flex" :class="zoomed ? 'fixed bottom-0 left-3 {{ config('rapidez.z-indexes.lightbox') }}' : ' flex-wrap mt-3'">
<div v-if="media.length > 1" class="flex" :class="zoomed ? 'fixed bottom-0 left-3 {{ config('rapidez.z-indexes.lightbox') }}' : ' flex-wrap mt-3'">
<button
v-for="(image, imageId) in images"
class="flex items-center justify-center bg-white border rounded p-2 mr-3 mb-3 h-[100px] w-[100px]"
v-for="(mediaItem, imageId) in media"
class="flex items-center justify-center bg-white border rounded p-2 mr-3 mb-3 h-[100px] w-[100px] relative"
:class="active == imageId ? 'border-neutral' : ''"
@click="change(imageId)"
>
<img
:src="'/storage/{{ config('rapidez.store') }}/resizes/80x80/magento/catalog/product' + image + '.webp'"
v-if="mediaItem.media_type === 'image'"
:src="'/storage/{{ config('rapidez.store') }}/resizes/80x80/magento/catalog/product' + mediaItem.image + '.webp'"
alt="{{ $product->name }}"
class="object-contain w-full m-auto max-h-[80px]"
loading="lazy"
width="80"
height="80"
/>
<template v-else-if="mediaItem.media_type === 'external-video'">
<img
:src="window.config.media_url + '/catalog/product' + mediaItem.image"
alt="{{ $product->name }}"
class="object-contain w-full m-auto max-h-[80px]"
loading="lazy"
width="80"
height="80"
/>
<x-heroicon-s-play class="absolute inset-5 text-white opacity-75"/>
</template>
</button>
</div>
<div v-if="zoomed" class="{{ config('rapidez.frontend.z-indexes.lightbox') }} pointer-events-none fixed top-3 right-3">
Expand Down
1 change: 1 addition & 0 deletions src/Http/Controllers/ProductController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function show(int $productId)
'price',
'special_price',
'images',
'product_gallery',
'url',
'min_sale_qty',
'qty_increments',
Expand Down
12 changes: 12 additions & 0 deletions src/Models/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Support\Str;
use Rapidez\Core\Casts\Children;
use Rapidez\Core\Casts\CommaSeparatedToArray;
use Rapidez\Core\Casts\CommaSeparatedToIntegerArray;
Expand Down Expand Up @@ -210,6 +211,17 @@ public function getImagesAttribute(): array
return $this->gallery->sortBy('productImageValue.position')->pluck('value')->toArray();
}

public function getProductGalleryAttribute(): array
{
return $this->gallery->sortBy('productImageValue.position')->map(function (ProductImage $value) {
return [
'media_type' => $value->media_type,
'image' => $value->value,
'video_url' => isset($value->video) ? Str::embedUrl($value->video->url) : null,
];
})->values()->toArray();
}

public function getImageAttribute($image): ?string
{
return $image !== 'no_selection' ? $image : null;
Expand Down
7 changes: 6 additions & 1 deletion src/Models/ProductImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ProductImage extends Model

protected $primaryKey = 'value_id';

protected $with = ['productImageValue'];
protected $with = ['productImageValue', 'video'];

protected static function booted(): void
{
Expand All @@ -30,4 +30,9 @@ public function productImageValue()
{
return $this->belongsTo(config('rapidez.models.product_image_value'), 'value_id', 'value_id');
}

public function video()
{
return $this->belongsTo(config('rapidez.models.product_video_value'), 'value_id', 'value_id');
}
}
10 changes: 10 additions & 0 deletions src/Models/ProductVideoValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Rapidez\Core\Models;

class ProductVideoValue extends Model
{
protected $table = 'catalog_product_entity_media_gallery_value_video';

protected $primaryKey = 'value_id';
}
72 changes: 72 additions & 0 deletions src/RapidezServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,78 @@ protected function bootMacros(): self
return Str::squish($this->render());
});

Str::macro(
'embedUrl',
function ($url) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is taken from Statamic, since it is not installed by default we utilise this macro

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be aware that the license within Statamic does not necessarily allow for direct copies of its code without direct consent: https://github.com/statamic/cms/blob/5.x/LICENSE.md

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please double check with @jackmcdade 😇 and add some love notes if it's ok to copy-paste ❤️

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go for it, it's nothing particularly proprietary – just a bunch of string manipulation. Merry Christmas! 🎄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Thanks Jack, Merry Christmas 🚀

if (Str::contains($url, 'vimeo')) {
$url = str_replace('/vimeo.com', '/player.vimeo.com/video', $url);

// Handle private vimeo urls.
$hash = null;
if (! Str::contains($url, 'progressive_redirect') && Str::substrCount($url, '/') > 4) {
$hash = Str::afterLast($url, '/');
$url = Str::beforeLast($url, '/');

if (Str::contains($hash, '?')) {
$url .= '?' . Str::after($hash, '?');
$hash = Str::before($hash, '?');
}
}

$paramsToAdd = '?dnt=1&watch_full_video=false&vimeo_logo=false&speed=false&chromecast=false&byline=false&badge=false&ask_ai=false&airplay=false';
if ($hash) {
$paramsToAdd .= '&h=' . $hash;
}

if (Str::contains($url, '?')) {
$url = str_replace('?', $paramsToAdd . '&', $url);
} else {
$url .= $paramsToAdd;
}

return $url;
}

if (Str::contains($url, 'youtu.be')) {
$url = str_replace('youtu.be', 'www.youtube.com/embed', $url);

// Check for start at point and replace it with correct parameter.
if (Str::contains($url, '?t=')) {
$url = str_replace('?t=', '?start=', $url);
}
}

if (Str::contains($url, 'youtube.com/watch?v=')) {
$url = str_replace('watch?v=', 'embed/', $url);

if (Str::contains($url, '&t=')) {
$url = str_replace('&t=', '?start=', $url);
}
}

if (Str::contains($url, 'youtube.com/shorts/')) {
$url = str_replace('shorts/', 'embed/', $url);
}

if (Str::contains($url, 'youtube.com')) {
$url = str_replace('youtube.com', 'youtube-nocookie.com', $url);
}

// This avoids SSL issues when using the non-www version
if (Str::contains($url, '//youtube-nocookie.com')) {
$url = str_replace('//youtube-nocookie.com', '//www.youtube-nocookie.com', $url);
}

$url .= '&fs=0';

if (Str::contains($url, '&') && ! Str::contains($url, '?')) {
$url = Str::replaceFirst('&', '?', $url);
}

return $url;
}
);

return $this;
}

Expand Down