Skip to content

Commit b25ed88

Browse files
committed
add a view page for qr code link
1 parent ebd2b87 commit b25ed88

File tree

6 files changed

+242
-1
lines changed

6 files changed

+242
-1
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Standalone viewer page (view.php)
2+
3+
:root {
4+
// mirror gallery style tokens
5+
--viewer-background: var(--primary-light-color);
6+
--viewer-foreground: var(--font-color, #111);
7+
--viewer-surface: #ffffff;
8+
--viewer-accent: var(--secondary-color, #444);
9+
--viewer-accent-foreground: var(--secondary-font-color, #fff);
10+
--viewer-shadow: 0 18px 48px rgba(0, 0, 0, 0.25);
11+
}
12+
13+
.viewer-page {
14+
margin: 0;
15+
min-height: 100vh;
16+
background:
17+
radial-gradient(circle at 20% 20%, color-mix(in srgb, var(--viewer-background), white 10%), transparent 32%),
18+
radial-gradient(circle at 85% 10%, color-mix(in srgb, var(--primary-color, #2196f3), white 12%), transparent 28%),
19+
linear-gradient(
20+
135deg,
21+
color-mix(in srgb, var(--viewer-background), var(--primary-color, #2196f3) 35%),
22+
var(--primary-color, #2196f3)
23+
);
24+
font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
25+
color: var(--viewer-foreground);
26+
display: flex;
27+
align-items: center;
28+
justify-content: center;
29+
padding: 24px;
30+
}
31+
32+
.viewer {
33+
width: min(960px, 100%);
34+
background: rgba(255, 255, 255, 0.12);
35+
backdrop-filter: blur(12px);
36+
border-radius: 18px;
37+
box-shadow: var(--viewer-shadow);
38+
padding: clamp(16px, 3vw, 24px);
39+
border: 1px solid rgba(255, 255, 255, 0.12);
40+
}
41+
42+
.viewer__inner {
43+
background: var(--viewer-surface);
44+
border-radius: 14px;
45+
padding: clamp(14px, 3vw, 22px);
46+
display: flex;
47+
flex-direction: column;
48+
gap: 16px;
49+
border: 1px solid rgba(0, 0, 0, 0.05);
50+
}
51+
52+
.viewer__header {
53+
display: flex;
54+
flex-direction: column;
55+
align-items: center;
56+
gap: 10px;
57+
}
58+
59+
.viewer__title {
60+
margin: 0;
61+
width: 100%;
62+
text-align: center;
63+
display: flex;
64+
flex-direction: column;
65+
gap: 0.2em;
66+
}
67+
68+
.viewer__title-line {
69+
font-size: clamp(22px, 5vw, 34px);
70+
color: var(--viewer-accent);
71+
line-height: 1.05;
72+
font-weight: 700;
73+
letter-spacing: 0.01em;
74+
}
75+
76+
.viewer__accent {
77+
height: 6px;
78+
width: 100%;
79+
border-radius: 999px;
80+
background: linear-gradient(90deg, var(--primary-color, #2196f3), var(--secondary-color, #3f51b5));
81+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
82+
}
83+
84+
.viewer__badge {
85+
background: var(--viewer-accent);
86+
color: var(--viewer-accent-foreground);
87+
padding: 6px 10px;
88+
border-radius: 999px;
89+
font-size: 12px;
90+
letter-spacing: 0.04em;
91+
text-transform: uppercase;
92+
}
93+
94+
.viewer__media {
95+
position: relative;
96+
border-radius: 12px;
97+
overflow: hidden;
98+
background: #f5f5f5;
99+
border: 2px solid rgba(0, 0, 0, 0.05);
100+
max-height: 70vh;
101+
display: grid;
102+
place-items: center;
103+
box-shadow: inset 0 12px 22px rgba(0, 0, 0, 0.04);
104+
}
105+
106+
.viewer__media img,
107+
.viewer__media video {
108+
display: block;
109+
width: 100%;
110+
height: auto;
111+
object-fit: contain;
112+
}
113+
114+
.viewer__media video {
115+
background: #000;
116+
}
117+
118+
.viewer__btn {
119+
min-height: 56px;
120+
touch-action: manipulation;
121+
user-select: none;
122+
-webkit-tap-highlight-color: transparent;
123+
padding-inline: 2.2rem;
124+
width: 100%;
125+
}
126+
127+
.viewer__tip {
128+
margin: 0;
129+
font-size: 14px;
130+
color: rgba(0, 0, 0, 0.64);
131+
text-align: center;
132+
}
133+
134+
@media (min-width: 720px) {
135+
.viewer__actions {
136+
grid-template-columns: repeat(2, 1fr);
137+
}
138+
}
139+
140+
@media (max-width: 540px) {
141+
.viewer {
142+
padding: 12px;
143+
}
144+
145+
.viewer__inner {
146+
padding: 14px;
147+
}
148+
149+
.viewer__title {
150+
font-size: clamp(18px, 6vw, 26px);
151+
}
152+
}

assets/sass/framework.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
@use 'components/virtualKeyboard';
2424
@use 'components/github-corner';
2525
@use 'components/background';
26+
@use 'components/viewer';
2627

2728
// Experiments
2829
@use 'experiments/video-capture-animation';

resources/lang/de.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,9 @@
853853
"qr:qr_text": "Eigener Hilfetext",
854854
"qr:qr_url": "URL für QR-Code",
855855
"qrHelp": "Um das Bild auf Ihr Handy herunterzuladen, verbinden Sie sich mit dem WLAN:",
856+
"share": "Teilen",
857+
"viewer_photo_title": "Dein Foto",
858+
"viewer_video_fallback": "Dein Browser kann dieses Video nicht abspielen.",
856859
"really_delete": "Wirklich nach Ihren Einstellungen zurücksetzen? Dies kann nicht rückgängig gemacht werden!",
857860
"really_delete_image": "wird gelöscht! Dies kann nicht rückgängig gemacht werden! Bild wirklich löschen?",
858861
"reboot_button": "Neustart",

resources/lang/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,9 @@
855855
"qr:qr_text": "Own help text",
856856
"qr:qr_url": "URL for QR Code",
857857
"qrHelp": "To download the picture to your smartphone, connect to the WiFi:",
858+
"share": "Share",
859+
"viewer_photo_title": "Your photo",
860+
"viewer_video_fallback": "Your browser can’t play this video.",
858861
"really_delete": "Really reset according to your settings? This cannot be undone!",
859862
"really_delete_image": "will be deleted! This cannot be undone! Really delete picture?",
860863
"reboot_button": "Reboot",

src/Service/ConfigurationService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ protected function addDefaults(array $config): array
120120
}
121121

122122
if (empty($config['qr']['url'])) {
123-
$config['qr']['url'] = 'api/download.php?image=';
123+
$config['qr']['url'] = 'view.php?image=';
124124
}
125125

126126
return $config;

view.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
use Photobooth\Enum\FolderEnum;
4+
use Photobooth\Service\ApplicationService;
5+
use Photobooth\Service\LanguageService;
6+
use Photobooth\Utility\ComponentUtility;
7+
use Photobooth\Utility\PathUtility;
8+
9+
require_once __DIR__ . '/lib/boot.php';
10+
11+
$imageParam = $_GET['image'] ?? '';
12+
$image = basename((string) $imageParam);
13+
14+
if ($image === '') {
15+
http_response_code(400);
16+
echo 'No image specified.';
17+
exit();
18+
}
19+
20+
$imagePath = FolderEnum::IMAGES->absolute() . DIRECTORY_SEPARATOR . $image;
21+
if (!is_file($imagePath)) {
22+
http_response_code(404);
23+
echo 'Image not found.';
24+
exit();
25+
}
26+
27+
$extension = strtolower(pathinfo($imagePath, PATHINFO_EXTENSION));
28+
$isVideo = in_array($extension, ['mp4', 'mov', 'webm'], true);
29+
$mime = match ($extension) {
30+
'png' => 'image/png',
31+
'gif' => 'image/gif',
32+
default => 'image/jpeg',
33+
};
34+
$imageUrl = PathUtility::getPublicPath(FolderEnum::IMAGES->value . '/' . rawurlencode($image));
35+
$downloadUrl = PathUtility::getPublicPath('api/download.php?image=' . rawurlencode($image));
36+
$languageService = LanguageService::getInstance();
37+
$pageTitle = ApplicationService::getInstance()->getTitle() . ' - ' . $languageService->translate('viewer_photo_title');
38+
$photoswipe = false;
39+
$remoteBuzzer = false;
40+
41+
include PathUtility::getAbsolutePath('template/components/main.head.php');
42+
?>
43+
<body class="viewer-page">
44+
<main class="viewer">
45+
<div class="viewer__inner">
46+
<header class="viewer__header">
47+
<div class="viewer__title">
48+
<?php if ($config['event']['enabled']): ?>
49+
<span class="viewer__title-line"><?= htmlspecialchars($config['event']['textLeft']) ?></span>
50+
<?php if (!empty($config['event']['symbol'])): ?>
51+
<span class="viewer__title-line">
52+
<i class="fa <?= htmlspecialchars($config['event']['symbol']) ?>" aria-hidden="true"></i>
53+
</span>
54+
<?php endif; ?>
55+
<span class="viewer__title-line"><?= htmlspecialchars($config['event']['textRight']) ?></span>
56+
<?php else: ?>
57+
<span class="viewer__title-line"><?= htmlspecialchars(ApplicationService::getInstance()->getTitle()) ?></span>
58+
<?php endif; ?>
59+
</div>
60+
</header>
61+
<div class="viewer__accent"></div>
62+
63+
<div class="viewer__media" aria-label="Captured media preview">
64+
<?php if ($isVideo): ?>
65+
<video src="<?=$imageUrl?>" controls playsinline controlsList="nodownload">
66+
<?=htmlspecialchars($languageService->translate('viewer_video_fallback'))?>
67+
</video>
68+
<?php else: ?>
69+
<img id="viewer-image" src="<?=$imageUrl?>" alt="Captured photo">
70+
<?php endif; ?>
71+
</div>
72+
73+
<div class="viewer__actions buttonbar">
74+
<?= ComponentUtility::renderButtonLink('download', $config['icons']['download'], $downloadUrl, true, ['download' => 'download']) ?>
75+
</div>
76+
77+
</div>
78+
</main>
79+
80+
<?php include PathUtility::getAbsolutePath('template/components/main.footer.php'); ?>
81+
</body>
82+
</html>

0 commit comments

Comments
 (0)