diff --git a/src/assets/index.css b/src/assets/index.css index 56876f2..689042e 100644 --- a/src/assets/index.css +++ b/src/assets/index.css @@ -255,6 +255,12 @@ gap: 0.75rem; } +/* A notification takes touches even while a dialog is open: the dialog turns off pointer events on the page, and + the notifications would inherit that, so they could not be swiped away. */ +.harbor-toaster [data-sonner-toast]:not([data-visible='false']) { + pointer-events: auto; +} + .harbor-toaster [data-sonner-toast] [data-title] { color: rgb(var(--harbor-ink)); font-size: 0.9375rem; @@ -393,6 +399,9 @@ /* Whole bars: the tabs plus the bar's padding on both sides. */ --liquid-bar-height: calc(var(--ui-tab-size) + 12px); --liquid-bar-height-sm: calc(var(--ui-tab-size-sm) + 8px); + /* The phone tab bar's gap to the screen bottom: 1rem, or a little inside the home indicator area where the page + covers it (the OpenMeet app), so the bar sits close to the edge. */ + --liquid-bar-bottom: max(1rem, calc(env(safe-area-inset-bottom) - 0.5rem)); /* Meeting controls and the meeting's top row buttons, in normal and full screen. */ /* Large screens stop growing them at 52px, so the bar stays compact on big monitors. */ --meeting-control-size: clamp(44px, calc(30px + 2.6vmin), 52px); @@ -1720,9 +1729,9 @@ } } - /* The call screen is always dark, so it and its chrome (controls, phone bars, chat, camera preview) use a dark - Harbor palette. `meeting-immersive` sits on the document while a call is on screen, because the chat and the - camera preview open in portals. */ + /* The call screen is always dark, so it and its chrome (controls, phone bars, chat, camera preview, and the + notifications shown during the call) use a dark Harbor palette. `meeting-immersive` sits on the document while + a call is on screen, because the chat, the camera preview, and the notifications render outside it. */ .meeting-controls-dark, .call-screen-dark, .meeting-immersive @@ -1731,7 +1740,8 @@ [data-call-phone-top-bar], .harbor-chat-panel, [data-camera-preview], - [data-meeting-invite] + [data-meeting-invite], + .harbor-toaster ) { --harbor-ink: 233 237 239; --harbor-ink-soft: 209 215 219; @@ -1792,6 +1802,11 @@ border-top: 1px solid rgb(var(--harbor-line)); } + /* A closed expander has no height, so it draws no divider either. */ + .call-panel-group > [data-expand][data-state='closed'] { + border-top-width: 0; + } + .call-panel-row { display: flex; width: 100%; diff --git a/src/components/dashboard-page/__tests__/ChatPane.test.ts b/src/components/dashboard-page/__tests__/ChatPane.test.ts index 61f7872..c3e40a4 100644 --- a/src/components/dashboard-page/__tests__/ChatPane.test.ts +++ b/src/components/dashboard-page/__tests__/ChatPane.test.ts @@ -121,6 +121,18 @@ describe('ChatPane', () => { expect(wrapper.find('[data-presence-dot]').exists()).toBe(false); }); + it('pads the header for the status bar and the composer for the home indicator, except while typing', async () => { + const wrapper = mountPane({ conversation }); + expect(wrapper.get('header').classes().join(' ')).toContain('env(safe-area-inset-top)'); + const bar = wrapper.get('[data-composer-bar]'); + expect(bar.classes().join(' ')).toContain('env(safe-area-inset-bottom)'); + + await bar.trigger('focusin'); + expect(bar.classes()).toContain('pb-2.5'); + await bar.trigger('focusout'); + expect(bar.classes().join(' ')).toContain('env(safe-area-inset-bottom)'); + }); + it('starts video and audio calls from the header actions', async () => { const wrapper = mountPane({ conversation, diff --git a/src/components/dashboard-page/calls/CallsSidebar.vue b/src/components/dashboard-page/calls/CallsSidebar.vue index 5b9d871..44e3f00 100644 --- a/src/components/dashboard-page/calls/CallsSidebar.vue +++ b/src/components/dashboard-page/calls/CallsSidebar.vue @@ -1,9 +1,8 @@ + + diff --git a/src/components/meeting-page/CallPanelChoice.vue b/src/components/meeting-page/CallPanelChoice.vue index 50f1b44..00e0137 100644 --- a/src/components/meeting-page/CallPanelChoice.vue +++ b/src/components/meeting-page/CallPanelChoice.vue @@ -2,6 +2,8 @@ import { Check, ChevronDown } from 'lucide-vue-next'; import { computed, useId } from 'vue'; +import { Expand } from '@/components/ui/expand'; + // One setting in the phone controls panel, such as a camera's source or quality: a row with the setting's name // over its current value, which expands in place to the choices. export interface CallPanelOption { @@ -51,28 +53,30 @@ const current = computed(() => props.options.find((option) => option.value === p aria-hidden="true" /> -
-

{{ emptyText }}

- -
+ +
+

{{ emptyText }}

+ +
+
diff --git a/src/components/meeting-page/CallPhoneControls.vue b/src/components/meeting-page/CallPhoneControls.vue index fc1bef2..e4a4fce 100644 --- a/src/components/meeting-page/CallPhoneControls.vue +++ b/src/components/meeting-page/CallPhoneControls.vue @@ -25,6 +25,11 @@ defineProps<{ /** The call could not get the microphone or camera; its button shows a warning that asks again. */ audioWarning?: boolean; videoWarning?: boolean; + /** + * Full screen sits the bar 0.25rem higher, so it clears the screen's edge with a little more room. Over the home + * indicator area (the OpenMeet app) the bar always keeps 0.5rem above the indicator. + */ + fullscreen?: boolean; }>(); const emit = defineEmits<{ @@ -163,7 +168,12 @@ function onLeave() { diff --git a/src/components/meeting-page/MeetingControls.vue b/src/components/meeting-page/MeetingControls.vue index dfb83f0..344ec28 100644 --- a/src/components/meeting-page/MeetingControls.vue +++ b/src/components/meeting-page/MeetingControls.vue @@ -76,8 +76,11 @@ function onLeave() {
diff --git a/src/components/meeting-page/MeetingInviteContent.vue b/src/components/meeting-page/MeetingInviteContent.vue index 3d80509..e650927 100644 --- a/src/components/meeting-page/MeetingInviteContent.vue +++ b/src/components/meeting-page/MeetingInviteContent.vue @@ -167,7 +167,13 @@ watch(policy, () => { class="h-11 rounded-xl border border-harbor-line bg-harbor-canvas pl-9 text-sm focus-visible:ring-0" />
- + +