Aufgabenkarten in der Gäste-pwa als swipe-barer Stapel umgesetzt. Sofortiges Freigeben von Foto-Uploads als Event-Einstellung implementiert.
This commit is contained in:
@@ -490,7 +490,7 @@ class EventPublicController extends BaseController
|
||||
])
|
||||
->orderByRaw('COALESCE(event_task_collection.sort_order, event_task.sort_order, tasks.sort_order, 0)')
|
||||
->orderBy('tasks.sort_order')
|
||||
->limit(20)
|
||||
->distinct('tasks.id')
|
||||
->get();
|
||||
|
||||
$tasks = $rows->map(function ($row) use ($fallbacks) {
|
||||
@@ -659,6 +659,22 @@ class EventPublicController extends BaseController
|
||||
return $trimmed;
|
||||
}
|
||||
|
||||
private function buildDeterministicSeed(?string $identifier): int
|
||||
{
|
||||
if ($identifier === null || trim($identifier) === '') {
|
||||
return random_int(1, PHP_INT_MAX);
|
||||
}
|
||||
|
||||
$hash = substr(sha1($identifier), 0, 8);
|
||||
$seed = hexdec($hash);
|
||||
|
||||
if (! is_numeric($seed) || $seed <= 0) {
|
||||
$seed = abs(crc32($identifier));
|
||||
}
|
||||
|
||||
return max(1, (int) $seed);
|
||||
}
|
||||
|
||||
private function buildAchievementsPayload(int $eventId, ?string $guestIdentifier, array $fallbacks): array
|
||||
{
|
||||
$totalPhotos = (int) DB::table('photos')->where('event_id', $eventId)->count();
|
||||
@@ -1757,6 +1773,7 @@ class EventPublicController extends BaseController
|
||||
'join_token' => $joinToken?->token,
|
||||
'photobooth_enabled' => (bool) $event->photobooth_enabled,
|
||||
'branding' => $branding,
|
||||
'guest_upload_visibility' => Arr::get($event->settings ?? [], 'guest_upload_visibility', 'review'),
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
@@ -2443,7 +2460,27 @@ class EventPublicController extends BaseController
|
||||
});
|
||||
|
||||
$tasks = $cached['tasks'];
|
||||
$etag = $cached['hash'] ?? sha1(json_encode($tasks));
|
||||
$baseHash = $cached['hash'] ?? sha1(json_encode($tasks));
|
||||
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$perPage = max(1, min(100, (int) $request->query('per_page', 20)));
|
||||
|
||||
// Shuffle per request for unpredictability; stable when seeded by guest/device or explicit seed.
|
||||
$seedParam = $request->query('seed');
|
||||
$guestIdentifier = $this->determineGuestIdentifier($request);
|
||||
$seedValue = is_numeric($seedParam)
|
||||
? (int) $seedParam
|
||||
: $this->buildDeterministicSeed(($guestIdentifier ? $guestIdentifier.'|' : '').(string) $event->id);
|
||||
$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937($seedValue));
|
||||
$shuffled = $randomizer->shuffleArray($tasks);
|
||||
|
||||
$total = count($shuffled);
|
||||
$offset = ($page - 1) * $perPage;
|
||||
$data = array_slice($shuffled, $offset, $perPage);
|
||||
$lastPage = (int) ceil($total / $perPage);
|
||||
$hasMore = $page < $lastPage;
|
||||
|
||||
$etag = sha1($baseHash . ':' . $page . ':' . $perPage . ':' . $seedValue);
|
||||
$reqEtag = $request->headers->get('If-None-Match');
|
||||
|
||||
if ($reqEtag && $reqEtag === $etag) {
|
||||
@@ -2454,7 +2491,19 @@ class EventPublicController extends BaseController
|
||||
->header('ETag', $etag);
|
||||
}
|
||||
|
||||
return response()->json($tasks)
|
||||
$payload = [
|
||||
'data' => $data,
|
||||
'meta' => [
|
||||
'total' => $total,
|
||||
'per_page' => $perPage,
|
||||
'current_page' => $page,
|
||||
'last_page' => $lastPage,
|
||||
'has_more' => $hasMore,
|
||||
'seed' => $seedValue,
|
||||
],
|
||||
];
|
||||
|
||||
return response()->json($payload)
|
||||
->header('Cache-Control', 'public, max-age=120')
|
||||
->header('ETag', $etag)
|
||||
->header('Vary', 'Accept-Language, X-Locale')
|
||||
@@ -2629,6 +2678,8 @@ class EventPublicController extends BaseController
|
||||
'eventPackages.package',
|
||||
'storageAssignments.storageTarget',
|
||||
])->findOrFail($eventId);
|
||||
$uploadVisibility = Arr::get($eventModel->settings ?? [], 'guest_upload_visibility', 'review');
|
||||
$autoApproveUploads = $uploadVisibility === 'immediate';
|
||||
|
||||
$tenantModel = $eventModel->tenant;
|
||||
|
||||
@@ -2744,7 +2795,7 @@ class EventPublicController extends BaseController
|
||||
'thumbnail_path' => $thumbUrl,
|
||||
'likes_count' => 0,
|
||||
'ingest_source' => Photo::SOURCE_GUEST_PWA,
|
||||
'status' => 'pending',
|
||||
'status' => $autoApproveUploads ? 'approved' : 'pending',
|
||||
|
||||
// Handle emotion_id: prefer explicit ID, fallback to slug lookup, then default
|
||||
'emotion_id' => $this->resolveEmotionId($validated, $eventId),
|
||||
@@ -2827,10 +2878,14 @@ class EventPublicController extends BaseController
|
||||
$this->packageUsageTracker->recordPhotoUsage($eventPackage, $previousUsed, 1);
|
||||
}
|
||||
|
||||
$message = $autoApproveUploads
|
||||
? 'Photo uploaded and visible.'
|
||||
: 'Photo uploaded and pending review.';
|
||||
|
||||
$response = response()->json([
|
||||
'id' => $photoId,
|
||||
'status' => 'pending',
|
||||
'message' => 'Photo uploaded and pending review.',
|
||||
'status' => $autoApproveUploads ? 'approved' : 'pending',
|
||||
'message' => $message,
|
||||
], 201);
|
||||
|
||||
$this->recordTokenEvent(
|
||||
|
||||
@@ -45,6 +45,7 @@ class EventStoreRequest extends FormRequest
|
||||
'settings.branding' => ['nullable', 'array'],
|
||||
'settings.branding.*' => ['nullable'],
|
||||
'settings.engagement_mode' => ['nullable', Rule::in(['tasks', 'photo_only'])],
|
||||
'settings.guest_upload_visibility' => ['nullable', Rule::in(['review', 'immediate'])],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user