From 1d795e0acf07156703a58614a14bece52335798b Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 3 Jun 2026 22:47:22 +0200 Subject: [PATCH] Stage 8 polish: iPhone refinements (keyboard, native pickers, compact invite) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second owner-review pass (iPhone simulator): - Chat (and the modal) are sized in dvh so they shrink above the software keyboard, keeping the start of the conversation on screen instead of pushed off the top. - The profile away window returns to a native (the iOS wheel with 10-minute steps) instead of separate dropdowns; the timezone stays a native offset + +
+ {#each filteredFriends as f (f.accountId)} {/each} + {#if filteredFriends.length === 0}

{/if}
- -

{t('new.title')}

-
- {#each variants as v (v.id)} - - {/each} +
+ + +
- -

{t('new.moveTime')}

-
- {#each timeouts as to (to.secs)} - - {/each} -
- -

{t('new.hintsPerPlayer')}

-
- {#each [0, 1, 2] as h (h)} - - {/each} -
- - {/if} +
{/if} {/if} @@ -171,16 +178,13 @@ display: flex; flex-direction: column; gap: 14px; + height: 100%; + box-sizing: border-box; } .subtitle { color: var(--text-muted); margin: 0; } - h3 { - margin: 6px 0 0; - font-size: 0.9rem; - color: var(--text-muted); - } .variants { display: flex; flex-direction: column; @@ -219,7 +223,34 @@ color: var(--accent-text); border-color: var(--accent); } - .friends { + .fg { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + gap: 10px; + } + .picked { + display: flex; + flex-direction: column; + gap: 8px; + } + .ftitle { + font-size: 0.9rem; + color: var(--text-muted); + } + .search { + min-width: 0; + padding: 9px 11px; + border: 1px solid var(--border); + background: var(--surface); + color: var(--text); + border-radius: var(--radius-sm); + } + .friends-scroll { + flex: 1; + min-height: 0; + overflow-y: auto; display: flex; flex-direction: column; gap: 6px; @@ -233,8 +264,37 @@ background: var(--surface); border-radius: var(--radius-sm); } + .settings-row { + display: flex; + gap: 8px; + } + .field { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; + } + .field span { + font-size: 0.78rem; + color: var(--text-muted); + } + .field select { + width: 100%; + box-sizing: border-box; + min-width: 0; + padding: 9px; + border: 1px solid var(--border); + background: var(--surface); + color: var(--text); + border-radius: var(--radius-sm); + } + .muted { + color: var(--text-muted); + margin: 0; + } .invite { - margin-top: 8px; + flex: 0 0 auto; padding: 14px; border: 1px solid var(--accent); background: var(--accent); diff --git a/ui/src/screens/Profile.svelte b/ui/src/screens/Profile.svelte index 7be02d6..2a01ad0 100644 --- a/ui/src/screens/Profile.svelte +++ b/ui/src/screens/Profile.svelte @@ -5,8 +5,6 @@ import { t } from '../lib/i18n/index.svelte'; import { awayDurationOk, - awayHours, - awayMinutes, browserOffset, isOffsetZone, timezoneOffsets, @@ -17,10 +15,8 @@ let editing = $state(false); let dn = $state(''); let tz = $state('+00:00'); - let startH = $state('00'); - let startM = $state('00'); - let endH = $state('07'); - let endM = $state('00'); + let awayStart = $state('00:00'); + let awayEnd = $state('07:00'); let blockChat = $state(false); let blockFriendRequests = $state(false); let emailInput = $state(''); @@ -31,25 +27,21 @@ const b = browserOffset(); return timezoneOffsets.includes(b) ? b : '+00:00'; } - function splitTime(hhmm: string): [string, string] { - const m = /^(\d{2}):(\d{2})$/.exec(hhmm); - if (!m) return ['00', '00']; - return [m[1], awayMinutes.includes(m[2]) ? m[2] : '00']; + function clockOr(value: string, fallback: string): string { + return /^\d{2}:\d{2}$/.test(value) ? value : fallback; } function startEdit() { const p = app.profile!; dn = p.displayName; tz = isOffsetZone(p.timeZone) && timezoneOffsets.includes(p.timeZone) ? p.timeZone : defaultTz(); - [startH, startM] = splitTime(p.awayStart); - [endH, endM] = splitTime(p.awayEnd); + awayStart = clockOr(p.awayStart, '00:00'); + awayEnd = clockOr(p.awayEnd, '07:00'); blockChat = p.blockChat; blockFriendRequests = p.blockFriendRequests; editing = true; } - const awayStart = $derived(`${startH}:${startM}`); - const awayEnd = $derived(`${endH}:${endM}`); const nameOk = $derived(validDisplayName(dn)); const awayOk = $derived(awayDurationOk(awayStart, awayEnd)); const formValid = $derived(nameOk && awayOk); @@ -120,16 +112,8 @@
{t('profile.awayWindow')}
- {t('profile.from')} - - : - -
-
- {t('profile.to')} - - : - + +

{t('profile.awayHint')}

@@ -271,17 +255,22 @@ } .times { display: flex; - align-items: center; - gap: 8px; + gap: 12px; + } + .times label { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + } + .times input { + width: 100%; + box-sizing: border-box; } .tlabel { - min-width: 2.5em; color: var(--text-muted); font-size: 0.85rem; } - .colon { - font-weight: 700; - } .check { flex-direction: row !important; align-items: center;