avatar 2
All checks were successful
Deploy / deploy (push) Successful in 1m43s

This commit is contained in:
2026-07-27 01:50:38 +02:00
parent 08fffd24b6
commit 32985d5785
9 changed files with 216 additions and 79 deletions

View File

@@ -329,6 +329,70 @@ $sectionLinks = [
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
</div> </div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="avatarEyesVariant">Augen</label>
<select id="avatarEyesVariant" name="avatar_lorelei_eyes_variant" class="select" data-avatar-field="avatar_lorelei_eyes_variant">
<?php foreach ($avatarOptions['avatar_lorelei_eyes_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_eyes_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="stack gap-6">
<label class="label" for="avatarEyebrowsVariant">Augenbrauen</label>
<select id="avatarEyebrowsVariant" name="avatar_lorelei_eyebrows_variant" class="select" data-avatar-field="avatar_lorelei_eyebrows_variant">
<?php foreach ($avatarOptions['avatar_lorelei_eyebrows_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_eyebrows_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="avatarMouthVariant">Mund</label>
<select id="avatarMouthVariant" name="avatar_lorelei_mouth_variant" class="select" data-avatar-field="avatar_lorelei_mouth_variant">
<?php foreach ($avatarOptions['avatar_lorelei_mouth_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_mouth_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="stack gap-6">
<label class="label" for="avatarGlassesVariant">Brille</label>
<select id="avatarGlassesVariant" name="avatar_lorelei_glasses_variant" class="select" data-avatar-field="avatar_lorelei_glasses_variant">
<?php foreach ($avatarOptions['avatar_lorelei_glasses_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_glasses_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="avatarHairVariant">Haare</label>
<select id="avatarHairVariant" name="avatar_lorelei_hair_variant" class="select" data-avatar-field="avatar_lorelei_hair_variant">
<?php foreach ($avatarOptions['avatar_lorelei_hair_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_hair_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="stack gap-6">
<label class="label" for="avatarBeardVariant">Bart</label>
<select id="avatarBeardVariant" name="avatar_lorelei_beard_variant" class="select" data-avatar-field="avatar_lorelei_beard_variant">
<?php foreach ($avatarOptions['avatar_lorelei_beard_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_beard_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="avatarEarringsVariant">Ohrringe</label>
<select id="avatarEarringsVariant" name="avatar_lorelei_earrings_variant" class="select" data-avatar-field="avatar_lorelei_earrings_variant">
<?php foreach ($avatarOptions['avatar_lorelei_earrings_variant'] as $value => $label): ?>
<option value="<?= htmlspecialchars((string)$value, ENT_QUOTES) ?>" <?= $profile['avatar_lorelei_earrings_variant'] === $value ? 'selected' : '' ?>><?= htmlspecialchars((string)$label, ENT_QUOTES) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div> </div>
</div> </div>
<div class="flex gap-12"> <div class="flex gap-12">

View File

@@ -11,7 +11,7 @@ if ($isLoggedIn) {
if ($pdo) { if ($pdo) {
$profileSettings = new \App\ProfileSettings($pdo); $profileSettings = new \App\ProfileSettings($pdo);
$profileSettings->ensureSchema(); $profileSettings->ensureSchema();
$stmt = $pdo->prepare('SELECT display_name, first_name, avatar_preset, avatar_skin_tone, avatar_hair_style, avatar_hair_color, avatar_eye_color, avatar_beard_style, avatar_glasses_style, avatar_head_shape, avatar_hat_style, avatar_hat_color, avatar_outfit_style, avatar_outfit_color FROM user_profiles WHERE user_id = :id LIMIT 1'); $stmt = $pdo->prepare('SELECT display_name, first_name, avatar_preset, avatar_lorelei_eyes_variant, avatar_lorelei_eyebrows_variant, avatar_lorelei_mouth_variant, avatar_lorelei_glasses_variant, avatar_lorelei_hair_variant, avatar_lorelei_beard_variant, avatar_lorelei_earrings_variant FROM user_profiles WHERE user_id = :id LIMIT 1');
$stmt->execute(['id' => (int)$_SESSION['user_id']]); $stmt->execute(['id' => (int)$_SESSION['user_id']]);
$profileRow = $stmt->fetch(PDO::FETCH_ASSOC) ?: []; $profileRow = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
$displayName = trim((string)($profileRow['display_name'] ?? '')) ?: trim((string)($profileRow['first_name'] ?? '')) ?: 'Profil'; $displayName = trim((string)($profileRow['display_name'] ?? '')) ?: trim((string)($profileRow['first_name'] ?? '')) ?: 'Profil';

View File

@@ -740,7 +740,24 @@ document.addEventListener('DOMContentLoaded', () => {
if (!image) return; if (!image) return;
const checked = form.querySelector('[data-avatar-field="avatar_preset"]:checked'); const checked = form.querySelector('[data-avatar-field="avatar_preset"]:checked');
const url = checked?.getAttribute('data-avatar-url') || ''; const seed = checked?.value || '';
if (!seed) return;
const params = new URLSearchParams({ seed });
[
['avatar_lorelei_eyes_variant', 'eyesVariant'],
['avatar_lorelei_eyebrows_variant', 'eyebrowsVariant'],
['avatar_lorelei_mouth_variant', 'mouthVariant'],
['avatar_lorelei_glasses_variant', 'glassesVariant'],
['avatar_lorelei_hair_variant', 'hairVariant'],
['avatar_lorelei_beard_variant', 'beardVariant'],
['avatar_lorelei_earrings_variant', 'earringsVariant'],
].forEach(([field, param]) => {
const value = readValue(field);
if (value) params.set(param, value);
});
const url = `/api/avatar?${params.toString()}`;
if (!url) return; if (!url) return;
image.src = url; image.src = url;
}; };

View File

@@ -3,6 +3,7 @@ declare(strict_types=1);
use App\Avatar\Lorelei; use App\Avatar\Lorelei;
$allowedParams = ['eyesVariant', 'eyebrowsVariant', 'mouthVariant', 'glassesVariant', 'hairVariant', 'beardVariant', 'earringsVariant'];
$seed = Lorelei::normalizeSeed((string)($_GET['seed'] ?? 'papa-kind-treff')); $seed = Lorelei::normalizeSeed((string)($_GET['seed'] ?? 'papa-kind-treff'));
$cacheDir = dirname(__DIR__, 3) . '/assets/avatars/dicebear-cache'; $cacheDir = dirname(__DIR__, 3) . '/assets/avatars/dicebear-cache';
@@ -10,11 +11,20 @@ if (!is_dir($cacheDir)) {
@mkdir($cacheDir, 0775, true); @mkdir($cacheDir, 0775, true);
} }
$cacheFile = $cacheDir . '/' . $seed . '.svg'; $query = ['seed' => $seed, 'size' => '256'];
foreach ($allowedParams as $param) {
$value = Lorelei::normalizeVariant((string)($_GET[$param] ?? ''));
if ($value !== '') {
$query[$param] = $value;
}
}
$cacheKey = sha1(json_encode($query, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: $seed);
$cacheFile = $cacheDir . '/' . $cacheKey . '.svg';
$svg = is_file($cacheFile) ? file_get_contents($cacheFile) : false; $svg = is_file($cacheFile) ? file_get_contents($cacheFile) : false;
if ($svg === false) { if ($svg === false) {
$url = 'https://api.dicebear.com/10.x/lorelei/svg?seed=' . rawurlencode($seed) . '&size=256'; $url = 'https://api.dicebear.com/10.x/lorelei/svg?' . http_build_query($query);
$context = stream_context_create([ $context = stream_context_create([
'http' => [ 'http' => [
'method' => 'GET', 'method' => 'GET',

View File

@@ -31,17 +31,13 @@ CREATE TABLE user_profiles (
lng DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL,
location_tracking_preference ENUM('disabled','prompt','enabled') NOT NULL DEFAULT 'prompt', location_tracking_preference ENUM('disabled','prompt','enabled') NOT NULL DEFAULT 'prompt',
avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff', avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff',
avatar_skin_tone VARCHAR(24) NOT NULL DEFAULT 'warm', avatar_lorelei_eyes_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_hair_style VARCHAR(24) NOT NULL DEFAULT 'short', avatar_lorelei_eyebrows_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_hair_color VARCHAR(24) NOT NULL DEFAULT 'brown', avatar_lorelei_mouth_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_eye_color VARCHAR(24) NOT NULL DEFAULT 'brown', avatar_lorelei_glasses_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_beard_style VARCHAR(24) NOT NULL DEFAULT 'none', avatar_lorelei_hair_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_glasses_style VARCHAR(24) NOT NULL DEFAULT 'none', avatar_lorelei_beard_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_head_shape VARCHAR(24) NOT NULL DEFAULT 'round', avatar_lorelei_earrings_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_hat_style VARCHAR(24) NOT NULL DEFAULT 'none',
avatar_hat_color VARCHAR(24) NOT NULL DEFAULT 'navy',
avatar_outfit_style VARCHAR(24) NOT NULL DEFAULT 'tee',
avatar_outfit_color VARCHAR(24) NOT NULL DEFAULT 'slate',
contact_phone VARBINARY(512) NULL, contact_phone VARBINARY(512) NULL,
contact_email VARBINARY(512) NULL, contact_email VARBINARY(512) NULL,
profession VARBINARY(512) NULL, profession VARBINARY(512) NULL,

View File

@@ -341,22 +341,18 @@ final class AccountPages
'contact_phone' => '', 'contact_phone' => '',
'location_tracking_preference' => 'prompt', 'location_tracking_preference' => 'prompt',
'avatar_preset' => 'papa-kind-treff', 'avatar_preset' => 'papa-kind-treff',
'avatar_skin_tone' => 'warm', 'avatar_lorelei_eyes_variant' => '',
'avatar_hair_style' => 'short', 'avatar_lorelei_eyebrows_variant' => '',
'avatar_hair_color' => 'brown', 'avatar_lorelei_mouth_variant' => '',
'avatar_eye_color' => 'brown', 'avatar_lorelei_glasses_variant' => '',
'avatar_beard_style' => 'none', 'avatar_lorelei_hair_variant' => '',
'avatar_glasses_style' => 'none', 'avatar_lorelei_beard_variant' => '',
'avatar_head_shape' => 'round', 'avatar_lorelei_earrings_variant' => '',
'avatar_hat_style' => 'none',
'avatar_hat_color' => 'navy',
'avatar_outfit_style' => 'tee',
'avatar_outfit_color' => 'slate',
]; ];
if ($profileSettings) { if ($profileSettings) {
$profileSettings->ensureSchema(); $profileSettings->ensureSchema();
} }
$stmt = $pdo?->prepare('SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.zip, p.city, p.profession, p.languages, p.about, p.contact_phone, p.location_tracking_preference, p.avatar_preset, p.avatar_skin_tone, p.avatar_hair_style, p.avatar_hair_color, p.avatar_eye_color, p.avatar_beard_style, p.avatar_glasses_style, p.avatar_head_shape, p.avatar_hat_style, p.avatar_hat_color, p.avatar_outfit_style, p.avatar_outfit_color FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1'); $stmt = $pdo?->prepare('SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.zip, p.city, p.profession, p.languages, p.about, p.contact_phone, p.location_tracking_preference, p.avatar_preset, p.avatar_lorelei_eyes_variant, p.avatar_lorelei_eyebrows_variant, p.avatar_lorelei_mouth_variant, p.avatar_lorelei_glasses_variant, p.avatar_lorelei_hair_variant, p.avatar_lorelei_beard_variant, p.avatar_lorelei_earrings_variant FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1');
$stmt?->execute(['id' => $userId]); $stmt?->execute(['id' => $userId]);
$row = $stmt?->fetch(\PDO::FETCH_ASSOC); $row = $stmt?->fetch(\PDO::FETCH_ASSOC);
if ($row) { if ($row) {
@@ -422,6 +418,7 @@ final class AccountPages
if (!in_array($section, $allowedSections, true)) { if (!in_array($section, $allowedSections, true)) {
$section = 'profile'; $section = 'profile';
} }
$avatarOptions = Lorelei::options();
$avatarPresetOptions = Lorelei::seedChoices((string)$profile['avatar_preset']); $avatarPresetOptions = Lorelei::seedChoices((string)$profile['avatar_preset']);
return compact( return compact(
@@ -439,6 +436,7 @@ final class AccountPages
'communityApplication', 'communityApplication',
'communityCanApply', 'communityCanApply',
'communityRestrictions', 'communityRestrictions',
'avatarOptions',
'avatarPresetOptions', 'avatarPresetOptions',
'section', 'section',
'allowedSections' 'allowedSections'

View File

@@ -9,23 +9,30 @@ final class Lorelei
{ {
return [ return [
'avatar_preset' => 'papa-kind-treff', 'avatar_preset' => 'papa-kind-treff',
'avatar_skin_tone' => 'warm', 'avatar_lorelei_eyes_variant' => '',
'avatar_hair_style' => 'short', 'avatar_lorelei_eyebrows_variant' => '',
'avatar_hair_color' => 'brown', 'avatar_lorelei_mouth_variant' => '',
'avatar_eye_color' => 'brown', 'avatar_lorelei_glasses_variant' => '',
'avatar_beard_style' => 'none', 'avatar_lorelei_hair_variant' => '',
'avatar_glasses_style' => 'none', 'avatar_lorelei_beard_variant' => '',
'avatar_head_shape' => 'round', 'avatar_lorelei_earrings_variant' => '',
'avatar_hat_style' => 'none',
'avatar_hat_color' => 'navy',
'avatar_outfit_style' => 'tee',
'avatar_outfit_color' => 'slate',
]; ];
} }
public static function options(): array public static function options(): array
{ {
return []; $variants24 = self::variantOptions(24);
$optional24 = ['' => 'Automatisch'] + $variants24;
return [
'avatar_lorelei_eyes_variant' => $variants24,
'avatar_lorelei_eyebrows_variant' => $variants24,
'avatar_lorelei_mouth_variant' => $variants24,
'avatar_lorelei_glasses_variant' => $optional24,
'avatar_lorelei_hair_variant' => $optional24,
'avatar_lorelei_beard_variant' => $optional24,
'avatar_lorelei_earrings_variant' => $optional24,
];
} }
public static function selectColumns(string $alias = 'p'): string public static function selectColumns(string $alias = 'p'): string
@@ -44,9 +51,17 @@ final class Lorelei
foreach ($defaults as $key => $defaultValue) { foreach ($defaults as $key => $defaultValue) {
$value = (string)($input[$key] ?? $defaultValue); $value = (string)($input[$key] ?? $defaultValue);
$normalized[$key] = $key === 'avatar_preset' if ($key === 'avatar_preset') {
? self::normalizeSeed($value) $normalized[$key] = self::normalizeSeed($value);
: $value; continue;
}
if (str_starts_with($key, 'avatar_lorelei_')) {
$normalized[$key] = self::normalizeVariant($value);
continue;
}
$normalized[$key] = $value;
} }
return $normalized; return $normalized;
@@ -119,10 +134,38 @@ final class Lorelei
return '/api/avatar?seed=' . rawurlencode(self::normalizeSeed($seed)); return '/api/avatar?seed=' . rawurlencode(self::normalizeSeed($seed));
} }
public static function apiQuery(array $row): array
{
$avatar = self::normalize($row);
$query = ['seed' => $avatar['avatar_preset']];
$map = [
'avatar_lorelei_eyes_variant' => 'eyesVariant',
'avatar_lorelei_eyebrows_variant' => 'eyebrowsVariant',
'avatar_lorelei_mouth_variant' => 'mouthVariant',
'avatar_lorelei_glasses_variant' => 'glassesVariant',
'avatar_lorelei_hair_variant' => 'hairVariant',
'avatar_lorelei_beard_variant' => 'beardVariant',
'avatar_lorelei_earrings_variant' => 'earringsVariant',
];
foreach ($map as $key => $param) {
if ($avatar[$key] !== '') {
$query[$param] = $avatar[$key];
}
}
return $query;
}
public static function buildProxyUrl(array $row): string
{
return '/api/avatar?' . http_build_query(self::apiQuery($row));
}
public static function render(array $row, string $name = 'Mitglied', string $size = 'md'): string public static function render(array $row, string $name = 'Mitglied', string $size = 'md'): string
{ {
$avatar = self::normalize($row); $avatar = self::normalize($row);
$src = self::dicebearUrl($avatar['avatar_preset']); $src = self::buildProxyUrl($avatar);
return sprintf( return sprintf(
'<span class="%s" role="img" aria-label="%s"><img class="pkt-avatar__img" src="%s" alt=""></span>', '<span class="%s" role="img" aria-label="%s"><img class="pkt-avatar__img" src="%s" alt=""></span>',
@@ -153,4 +196,25 @@ final class Lorelei
htmlspecialchars($initials, ENT_QUOTES) htmlspecialchars($initials, ENT_QUOTES)
); );
} }
private static function variantOptions(int $count): array
{
$options = [];
for ($index = 1; $index <= $count; $index++) {
$key = sprintf('variant%02d', $index);
$options[$key] = 'Variante ' . $index;
}
return $options;
}
public static function normalizeVariant(string $value): string
{
$value = strtolower(trim($value));
if ($value === '') {
return '';
}
return preg_match('/^variant\d{2}$/', $value) === 1 ? $value : '';
}
} }

View File

@@ -152,7 +152,7 @@ final class Community
$where = $conditions ? ('AND ' . implode(' AND ', $conditions)) : ''; $where = $conditions ? ('AND ' . implode(' AND ', $conditions)) : '';
$avatarSelect = $this->hasColumn('user_profiles', 'avatar_outfit_color') $avatarSelect = $this->hasColumn('user_profiles', 'avatar_preset')
? ', ' . Lorelei::selectColumns('p') ? ', ' . Lorelei::selectColumns('p')
: ''; : '';
$sql = "SELECT ft.id, ft.title, ft.body, ft.created_at, ft.updated_at, $sql = "SELECT ft.id, ft.title, ft.body, ft.created_at, ft.updated_at,
@@ -208,7 +208,7 @@ final class Community
public function getThread(int $id): ?array public function getThread(int $id): ?array
{ {
$select = 'ft.*, p.display_name'; $select = 'ft.*, p.display_name';
if ($this->hasColumn('user_profiles', 'avatar_outfit_color')) { if ($this->hasColumn('user_profiles', 'avatar_preset')) {
$select .= ', ' . Lorelei::selectColumns('p'); $select .= ', ' . Lorelei::selectColumns('p');
} }
$join = ''; $join = '';
@@ -228,7 +228,7 @@ final class Community
public function listPosts(int $threadId): array public function listPosts(int $threadId): array
{ {
$select = 'fp.*, p.display_name'; $select = 'fp.*, p.display_name';
if ($this->hasColumn('user_profiles', 'avatar_outfit_color')) { if ($this->hasColumn('user_profiles', 'avatar_preset')) {
$select .= ', ' . Lorelei::selectColumns('p'); $select .= ', ' . Lorelei::selectColumns('p');
} }
if ($this->hasColumn('forum_posts', 'highlighted_at')) { if ($this->hasColumn('forum_posts', 'highlighted_at')) {

View File

@@ -32,17 +32,13 @@ final class ProfileSettings
$avatarColumns = [ $avatarColumns = [
'avatar_preset' => "ALTER TABLE user_profiles ADD COLUMN avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff' AFTER location_tracking_preference", 'avatar_preset' => "ALTER TABLE user_profiles ADD COLUMN avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff' AFTER location_tracking_preference",
'avatar_skin_tone' => "ALTER TABLE user_profiles ADD COLUMN avatar_skin_tone VARCHAR(24) NOT NULL DEFAULT 'warm' AFTER location_tracking_preference", 'avatar_lorelei_eyes_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_eyes_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_preset",
'avatar_hair_style' => "ALTER TABLE user_profiles ADD COLUMN avatar_hair_style VARCHAR(24) NOT NULL DEFAULT 'short' AFTER avatar_skin_tone", 'avatar_lorelei_eyebrows_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_eyebrows_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_eyes_variant",
'avatar_hair_color' => "ALTER TABLE user_profiles ADD COLUMN avatar_hair_color VARCHAR(24) NOT NULL DEFAULT 'brown' AFTER avatar_hair_style", 'avatar_lorelei_mouth_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_mouth_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_eyebrows_variant",
'avatar_eye_color' => "ALTER TABLE user_profiles ADD COLUMN avatar_eye_color VARCHAR(24) NOT NULL DEFAULT 'brown' AFTER avatar_hair_color", 'avatar_lorelei_glasses_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_glasses_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_mouth_variant",
'avatar_beard_style' => "ALTER TABLE user_profiles ADD COLUMN avatar_beard_style VARCHAR(24) NOT NULL DEFAULT 'none' AFTER avatar_eye_color", 'avatar_lorelei_hair_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_hair_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_glasses_variant",
'avatar_glasses_style' => "ALTER TABLE user_profiles ADD COLUMN avatar_glasses_style VARCHAR(24) NOT NULL DEFAULT 'none' AFTER avatar_beard_style", 'avatar_lorelei_beard_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_beard_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_hair_variant",
'avatar_head_shape' => "ALTER TABLE user_profiles ADD COLUMN avatar_head_shape VARCHAR(24) NOT NULL DEFAULT 'round' AFTER avatar_glasses_style", 'avatar_lorelei_earrings_variant' => "ALTER TABLE user_profiles ADD COLUMN avatar_lorelei_earrings_variant VARCHAR(24) NOT NULL DEFAULT '' AFTER avatar_lorelei_beard_variant",
'avatar_hat_style' => "ALTER TABLE user_profiles ADD COLUMN avatar_hat_style VARCHAR(24) NOT NULL DEFAULT 'none' AFTER avatar_head_shape",
'avatar_hat_color' => "ALTER TABLE user_profiles ADD COLUMN avatar_hat_color VARCHAR(24) NOT NULL DEFAULT 'navy' AFTER avatar_hat_style",
'avatar_outfit_style' => "ALTER TABLE user_profiles ADD COLUMN avatar_outfit_style VARCHAR(24) NOT NULL DEFAULT 'tee' AFTER avatar_hat_color",
'avatar_outfit_color' => "ALTER TABLE user_profiles ADD COLUMN avatar_outfit_color VARCHAR(24) NOT NULL DEFAULT 'slate' AFTER avatar_hat_color",
]; ];
foreach ($avatarColumns as $column => $sql) { foreach ($avatarColumns as $column => $sql) {
if (!$this->hasColumn('user_profiles', $column)) { if (!$this->hasColumn('user_profiles', $column)) {
@@ -93,33 +89,25 @@ final class ProfileSettings
$stmt = $this->pdo->prepare( $stmt = $this->pdo->prepare(
'UPDATE user_profiles 'UPDATE user_profiles
SET avatar_preset = :preset, SET avatar_preset = :preset,
avatar_skin_tone = :skin, avatar_lorelei_eyes_variant = :eyesVariant,
avatar_hair_style = :hairStyle, avatar_lorelei_eyebrows_variant = :eyebrowsVariant,
avatar_hair_color = :hairColor, avatar_lorelei_mouth_variant = :mouthVariant,
avatar_eye_color = :eyeColor, avatar_lorelei_glasses_variant = :glassesVariant,
avatar_beard_style = :beardStyle, avatar_lorelei_hair_variant = :hairVariant,
avatar_glasses_style = :glassesStyle, avatar_lorelei_beard_variant = :beardVariant,
avatar_head_shape = :headShape, avatar_lorelei_earrings_variant = :earringsVariant,
avatar_hat_style = :hatStyle,
avatar_hat_color = :hatColor,
avatar_outfit_style = :outfitStyle,
avatar_outfit_color = :outfitColor,
updated_at = NOW() updated_at = NOW()
WHERE user_id = :id' WHERE user_id = :id'
); );
$stmt->execute([ $stmt->execute([
'preset' => $avatar['avatar_preset'], 'preset' => $avatar['avatar_preset'],
'skin' => $avatar['avatar_skin_tone'], 'eyesVariant' => $avatar['avatar_lorelei_eyes_variant'],
'hairStyle' => $avatar['avatar_hair_style'], 'eyebrowsVariant' => $avatar['avatar_lorelei_eyebrows_variant'],
'hairColor' => $avatar['avatar_hair_color'], 'mouthVariant' => $avatar['avatar_lorelei_mouth_variant'],
'eyeColor' => $avatar['avatar_eye_color'], 'glassesVariant' => $avatar['avatar_lorelei_glasses_variant'],
'beardStyle' => $avatar['avatar_beard_style'], 'hairVariant' => $avatar['avatar_lorelei_hair_variant'],
'glassesStyle' => $avatar['avatar_glasses_style'], 'beardVariant' => $avatar['avatar_lorelei_beard_variant'],
'headShape' => $avatar['avatar_head_shape'], 'earringsVariant' => $avatar['avatar_lorelei_earrings_variant'],
'hatStyle' => $avatar['avatar_hat_style'],
'hatColor' => $avatar['avatar_hat_color'],
'outfitStyle' => $avatar['avatar_outfit_style'],
'outfitColor' => $avatar['avatar_outfit_color'],
'id' => $userId, 'id' => $userId,
]); ]);
} }