fix(android): apply env(safe-area-inset-bottom) so 3-button nav stops covering bottom-anchored UI
This commit is contained in:
parent
aca33f470d
commit
443213b4b6
6 changed files with 47 additions and 13 deletions
|
|
@ -228,8 +228,17 @@ export const handleBar = style({
|
||||||
// up over the inline form. The DirectSelfRow ending up immediately
|
// up over the inline form. The DirectSelfRow ending up immediately
|
||||||
// above the keyboard would block the user's view of the form they're
|
// above the keyboard would block the user's view of the form they're
|
||||||
// typing into.
|
// typing into.
|
||||||
|
//
|
||||||
|
// `paddingBottom: env(safe-area-inset-bottom)` lifts DirectSelfRow /
|
||||||
|
// WorkspaceFooter above Android 3-button nav in edge-to-edge mode.
|
||||||
|
// The inset zone paints in the curtain's `Background.Container` bg
|
||||||
|
// (curtain has `overflow: hidden`), so the system bar reads as a
|
||||||
|
// continuation of the curtain tone — no visible seam. The keyboard
|
||||||
|
// `height: 0; overflow: hidden` collapse above also clips the
|
||||||
|
// padding region — keyboard handling preserved.
|
||||||
export const bottomPinnedSlot = style({
|
export const bottomPinnedSlot = style({
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
paddingBottom: 'env(safe-area-inset-bottom, 0px)',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Segment button (Direct / Channels / Bots).
|
// Segment button (Direct / Channels / Bots).
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import { globalStyle, style } from '@vanilla-extract/css';
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
import { color, toRem } from 'folds';
|
import { color, toRem } from 'folds';
|
||||||
import {
|
import { Editor, EditorTextarea, EditorTextareaScroll } from '../../components/editor/Editor.css';
|
||||||
Editor,
|
|
||||||
EditorTextarea,
|
|
||||||
EditorTextareaScroll,
|
|
||||||
} from '../../components/editor/Editor.css';
|
|
||||||
import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
||||||
|
|
||||||
// Main chat composer — Dawn canon (stream-v2-dawn.jsx line 285-307): a
|
// Main chat composer — Dawn canon (stream-v2-dawn.jsx line 285-307): a
|
||||||
|
|
@ -28,6 +24,13 @@ export const ChatComposer = style({});
|
||||||
// slide/fade transition driven by the `data-hidden` attribute set from
|
// slide/fade transition driven by the `data-hidden` attribute set from
|
||||||
// React state. CSS class (not inline `transition`) so the
|
// React state. CSS class (not inline `transition`) so the
|
||||||
// `prefers-reduced-motion` media query can disable the motion.
|
// `prefers-reduced-motion` media query can disable the motion.
|
||||||
|
//
|
||||||
|
// `env(safe-area-inset-bottom)` for Android 3-button-nav clearance lives
|
||||||
|
// on the INNER `ChatComposer` div (see `RoomView.tsx`), not here. The
|
||||||
|
// wrapper is the `ResizeObserver` target for `composerHeight`, and
|
||||||
|
// `entry.contentRect` returns content-box (excludes the observed node's
|
||||||
|
// own padding); padding here would inflate border-box without growing
|
||||||
|
// the reported height, letting the last message slip behind the inset.
|
||||||
export const ComposerOverlay = style({
|
export const ComposerOverlay = style({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,13 @@ export function RoomView({ eventId }: { eventId?: string }) {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={css.ChatComposer}
|
className={css.ChatComposer}
|
||||||
|
// `calc(GAP + env(safe-area-inset-bottom))` on the bottom —
|
||||||
|
// see `ComposerOverlay` css for why the inset lives here
|
||||||
|
// and not on the wrapper.
|
||||||
style={{
|
style={{
|
||||||
padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} ${toRem(VOJO_HORSESHOE_GAP_PX)}`,
|
padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} calc(${toRem(
|
||||||
|
VOJO_HORSESHOE_GAP_PX
|
||||||
|
)} + env(safe-area-inset-bottom, 0px))`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tombstoneEvent ? (
|
{tombstoneEvent ? (
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ export const RoomViewTyping = style([
|
||||||
DefaultReset,
|
DefaultReset,
|
||||||
{
|
{
|
||||||
padding: `0 ${config.space.S500}`,
|
padding: `0 ${config.space.S500}`,
|
||||||
|
// Lift typing text above the Android 3-button nav for the
|
||||||
|
// composer-hidden window (thread drawer open / scroll-hide). The
|
||||||
|
// main composer sits at `bottom: 0` with its own inset and fully
|
||||||
|
// covers this strip at rest, so the duplicate inset is invisible
|
||||||
|
// in normal flow.
|
||||||
|
paddingBottom: 'env(safe-area-inset-bottom, 0px)',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
backgroundColor: color.SurfaceVariant.Container,
|
backgroundColor: color.SurfaceVariant.Container,
|
||||||
color: color.Surface.OnContainer,
|
color: color.Surface.OnContainer,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
import { color, config, toRem } from 'folds';
|
import { color, config, toRem } from 'folds';
|
||||||
import {
|
import { VOJO_HORSESHOE_GAP_PX, VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
||||||
VOJO_HORSESHOE_GAP_PX,
|
|
||||||
VOJO_HORSESHOE_RADIUS_PX,
|
|
||||||
} from '../../styles/horseshoe';
|
|
||||||
|
|
||||||
// Desktop wrapper for the resizable thread drawer. Sizing and the
|
// Desktop wrapper for the resizable thread drawer. Sizing and the
|
||||||
// absolutely-positioned resize handle live here; the inner aside
|
// absolutely-positioned resize handle live here; the inner aside
|
||||||
|
|
@ -231,9 +228,15 @@ export const ThreadCounterText = style({
|
||||||
// reapplied to the inner wrap in `ThreadDrawer.tsx` so the same
|
// reapplied to the inner wrap in `ThreadDrawer.tsx` so the same
|
||||||
// `globalStyle` rules (`Surface.Container` bg, 32px radius, dark
|
// `globalStyle` rules (`Surface.Container` bg, 32px radius, dark
|
||||||
// touch-hover gate) reach the Editor inside.
|
// touch-hover gate) reach the Editor inside.
|
||||||
|
//
|
||||||
|
// Bottom side adds `env(safe-area-inset-bottom)` so the Send button
|
||||||
|
// clears Android 3-button nav in edge-to-edge mode, mirroring the
|
||||||
|
// main `ChatComposer` in `RoomView.tsx`.
|
||||||
export const ThreadComposer = style({
|
export const ThreadComposer = style({
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} ${toRem(VOJO_HORSESHOE_GAP_PX)}`,
|
padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} calc(${toRem(
|
||||||
|
VOJO_HORSESHOE_GAP_PX
|
||||||
|
)} + env(safe-area-inset-bottom, 0px))`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bubble chrome itself lives in `Channel.css.ts` and applies via the
|
// Bubble chrome itself lives in `Channel.css.ts` and applies via the
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,16 @@ import { color } from 'folds';
|
||||||
// Bottom inset is owned per-component: surfaces that anchor
|
// Bottom inset is owned per-component: surfaces that anchor
|
||||||
// interactive content at the screen bottom and need 3-button-nav /
|
// interactive content at the screen bottom and need 3-button-nav /
|
||||||
// home-indicator clearance read `env(safe-area-inset-bottom)`
|
// home-indicator clearance read `env(safe-area-inset-bottom)`
|
||||||
// themselves (e.g. `SyncIndicator.css.ts`). The chat surface /
|
// themselves (e.g. `SyncIndicator.css.ts`,
|
||||||
// composer extend flush to `body_bottom` by design.
|
// `StreamHeader.bottomPinnedSlot` for DirectSelfRow / WorkspaceFooter,
|
||||||
|
// `RoomView.tsx` ChatComposer inner div, the `panelContent` rules in
|
||||||
|
// MobileSettings / ChannelsWorkspace / MobileMediaViewer horseshoes).
|
||||||
|
// The chat overlay wrapper (`ComposerOverlay`) itself stays `bottom: 0`
|
||||||
|
// flush — the inset lives on the inner card padding so
|
||||||
|
// `ResizeObserver.contentRect` keeps `composerHeight` in sync with
|
||||||
|
// the visible overlay height for `RoomTimeline.bottomOverlayHeight`.
|
||||||
|
// Bot widgets are responsible for their own gesture-pill clearance
|
||||||
|
// (see `BotShell.css.ts::Shell`).
|
||||||
globalStyle(':root', {
|
globalStyle(':root', {
|
||||||
vars: {
|
vars: {
|
||||||
'--vojo-safe-top': 'env(safe-area-inset-top, 0px)',
|
'--vojo-safe-top': 'env(safe-area-inset-top, 0px)',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue