reworked the guest pwa, modernized start and gallery page. added share link functionality.
This commit is contained in:
@@ -6,6 +6,7 @@ use App\Models\Event;
|
||||
use App\Models\EventJoinToken;
|
||||
use App\Models\EventMediaAsset;
|
||||
use App\Models\Photo;
|
||||
use App\Models\PhotoShareLink;
|
||||
use App\Services\Analytics\JoinTokenAnalyticsRecorder;
|
||||
use App\Services\EventJoinTokenService;
|
||||
use App\Services\Packages\PackageLimitEvaluator;
|
||||
@@ -585,6 +586,18 @@ class EventPublicController extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
private function makeShareAssetUrl(PhotoShareLink $shareLink, string $variant): string
|
||||
{
|
||||
return URL::temporarySignedRoute(
|
||||
'api.v1.photo-shares.asset',
|
||||
now()->addSeconds(self::SIGNED_URL_TTL_SECONDS),
|
||||
[
|
||||
'slug' => $shareLink->slug,
|
||||
'variant' => $variant,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function gallery(Request $request, string $token)
|
||||
{
|
||||
$locale = $request->query('locale', app()->getLocale());
|
||||
@@ -677,6 +690,170 @@ class EventPublicController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function createShareLink(Request $request, string $token, Photo $photo)
|
||||
{
|
||||
$resolved = $this->resolvePublishedEvent($request, $token, ['id']);
|
||||
|
||||
if ($resolved instanceof JsonResponse) {
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
/** @var array{0: object{id:int}} $resolved */
|
||||
[$eventRecord] = $resolved;
|
||||
|
||||
if ((int) $photo->event_id !== (int) $eventRecord->id) {
|
||||
return ApiError::response(
|
||||
'photo_not_shareable',
|
||||
'Photo Not Shareable',
|
||||
'The selected photo cannot be shared at this time.',
|
||||
Response::HTTP_NOT_FOUND,
|
||||
[
|
||||
'photo_id' => $photo->id,
|
||||
'event_id' => $eventRecord->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$deviceId = trim((string) ($request->header('X-Device-Id') ?? $request->input('device_id', '')));
|
||||
$ttlHours = max(1, (int) config('share-links.ttl_hours', 48));
|
||||
|
||||
$existing = PhotoShareLink::query()
|
||||
->where('photo_id', $photo->id)
|
||||
->when($deviceId !== '', fn ($query) => $query->where('created_by_device_id', $deviceId))
|
||||
->where('expires_at', '>', now())
|
||||
->latest('id')
|
||||
->first();
|
||||
|
||||
if ($existing && ! $existing->isExpired()) {
|
||||
$shareLink = $existing;
|
||||
} else {
|
||||
$shareLink = PhotoShareLink::create([
|
||||
'photo_id' => $photo->id,
|
||||
'slug' => PhotoShareLink::generateSlug(),
|
||||
'expires_at' => now()->addHours($ttlHours),
|
||||
'created_by_device_id' => $deviceId ?: null,
|
||||
'created_ip' => $request->ip(),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'slug' => $shareLink->slug,
|
||||
'expires_at' => $shareLink->expires_at?->toIso8601String(),
|
||||
'url' => url("/share/{$shareLink->slug}"),
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
public function shareLink(Request $request, string $slug)
|
||||
{
|
||||
$shareLink = PhotoShareLink::with(['photo.event', 'photo.emotion', 'photo.task'])
|
||||
->where('slug', $slug)
|
||||
->first();
|
||||
|
||||
if (! $shareLink || $shareLink->isExpired()) {
|
||||
return ApiError::response(
|
||||
'share_link_expired',
|
||||
'Link Expired',
|
||||
'This shared photo link is no longer available.',
|
||||
Response::HTTP_GONE,
|
||||
['slug' => $slug]
|
||||
);
|
||||
}
|
||||
|
||||
$shareLink->forceFill(['last_accessed_at' => now()])->save();
|
||||
|
||||
$photo = $shareLink->photo;
|
||||
$event = $photo->event;
|
||||
|
||||
if (! $event || $photo->status !== 'approved') {
|
||||
return ApiError::response(
|
||||
'photo_not_shareable',
|
||||
'Photo Not Shareable',
|
||||
'The shared photo is no longer available.',
|
||||
Response::HTTP_NOT_FOUND,
|
||||
['slug' => $slug]
|
||||
);
|
||||
}
|
||||
|
||||
$taskTitle = null;
|
||||
|
||||
if ($photo->task) {
|
||||
$taskTitle = $this->translateLocalized($photo->task->title, app()->getLocale(), '');
|
||||
if ($taskTitle === '') {
|
||||
$taskTitle = null;
|
||||
}
|
||||
}
|
||||
|
||||
$photoResource = [
|
||||
'id' => $photo->id,
|
||||
'title' => $taskTitle,
|
||||
'emotion' => $photo->emotion ? [
|
||||
'name' => $photo->emotion->name,
|
||||
'emoji' => $photo->emotion->emoji,
|
||||
] : null,
|
||||
'likes_count' => $photo->likes()->count(),
|
||||
'image_urls' => [
|
||||
'thumbnail' => $this->makeShareAssetUrl($shareLink, 'thumbnail'),
|
||||
'full' => $this->makeShareAssetUrl($shareLink, 'full'),
|
||||
],
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'slug' => $shareLink->slug,
|
||||
'expires_at' => $shareLink->expires_at?->toIso8601String(),
|
||||
'photo' => $photoResource,
|
||||
'event' => $event ? [
|
||||
'id' => $event->id,
|
||||
'name' => $event->name,
|
||||
'city' => $event->city,
|
||||
] : null,
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
public function shareLinkAsset(Request $request, string $slug, string $variant)
|
||||
{
|
||||
if (! in_array($variant, ['thumbnail', 'full'], true)) {
|
||||
return ApiError::response(
|
||||
'invalid_variant',
|
||||
'Invalid Variant',
|
||||
'The requested asset variant is not supported.',
|
||||
Response::HTTP_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$shareLink = PhotoShareLink::with(['photo.mediaAsset', 'photo.event.tenant'])
|
||||
->where('slug', $slug)
|
||||
->first();
|
||||
|
||||
if (! $shareLink || $shareLink->isExpired()) {
|
||||
return ApiError::response(
|
||||
'share_link_expired',
|
||||
'Link Expired',
|
||||
'This shared photo link is no longer available.',
|
||||
Response::HTTP_GONE,
|
||||
['slug' => $slug]
|
||||
);
|
||||
}
|
||||
|
||||
$photo = $shareLink->photo;
|
||||
$event = $photo->event;
|
||||
|
||||
if (! $event || $photo->status !== 'approved') {
|
||||
return ApiError::response(
|
||||
'photo_not_shareable',
|
||||
'Photo Not Shareable',
|
||||
'The shared photo is no longer available.',
|
||||
Response::HTTP_NOT_FOUND,
|
||||
['slug' => $slug]
|
||||
);
|
||||
}
|
||||
|
||||
$variantPreference = $variant === 'thumbnail'
|
||||
? ['thumbnail', 'original']
|
||||
: ['original'];
|
||||
|
||||
return $this->streamGalleryPhoto($event, $photo, $variantPreference, 'inline');
|
||||
}
|
||||
|
||||
public function galleryPhotoAsset(Request $request, string $token, int $photo, string $variant)
|
||||
{
|
||||
$resolved = $this->resolveGalleryEvent($request, $token);
|
||||
|
||||
Reference in New Issue
Block a user