diff --git a/frontend/src/App.vue b/frontend/src/App.vue index c980f76..81ab047 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1311,6 +1311,17 @@ onMounted(() => { if (developmentParameters.has('payphonePreview')) { openDevelopmentPayphonePreview() } + if (developmentParameters.has('notificationPreview')) { + window.setTimeout(() => { + notifications.show({ + appId: 'messages', + persistent: true, + route: '/apps/messages', + text: 'You still got that spare alternator?', + title: 'Tommy V', + }) + }, 250) + } } }) diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 4e2c2cf..50d3768 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -536,19 +536,62 @@ button { border: 0 !important; opacity: 0 !important; } +.phone-notification-provider { + --phone-notification-background: rgb(247 247 248 / 94%); + --phone-notification-shadow: + 0 10px 30px rgb(0 0 0 / 18%), inset 0 0.5px 0 rgb(255 255 255 / 90%); + position: absolute; + z-index: 85; + inset: 0; + pointer-events: none; +} +.phone-notification-provider.sky-ui-provider--dark { + --phone-notification-background: rgb(36 36 39 / 94%); + --phone-notification-shadow: + 0 12px 34px rgb(0 0 0 / 36%), inset 0 0.5px 0 rgb(255 255 255 / 16%); +} .phone-notification { + --phone-notification-drag-y: 0px; z-index: 85 !important; - top: 68px !important; - right: 8px !important; - left: 8px !important; + top: 51px !important; + right: 10px !important; + left: 10px !important; width: auto !important; margin-left: 0 !important; + pointer-events: auto; + background: var(--phone-notification-background) !important; + box-shadow: var(--phone-notification-shadow) !important; + transform: translate3d(0, var(--phone-notification-drag-y), 0); + transition: transform 280ms var(--sky-ease-out, ease-out); + will-change: transform; } .phone-notification__icon { width: 38px; height: 38px; + border-radius: 10px; object-fit: contain; } +.phone-notification__open { + position: absolute; + z-index: 2; + inset: 0; + padding: 0; + border: 0; + border-radius: inherit; + background: transparent; + cursor: pointer; + -webkit-tap-highlight-color: transparent; +} +.phone-notification__open:active { + background: var(--sky-pressed); +} +.phone-notification__open:focus-visible { + outline: 2px solid var(--sky-app-accent); + outline-offset: -3px; +} +.phone-notification.phone-notification--dragging { + transition: none !important; +} .phone-status-bar { position: absolute; z-index: 97; diff --git a/frontend/src/components/NotificationPhonePreview.vue b/frontend/src/components/NotificationPhonePreview.vue index cccefab..8585f6a 100644 --- a/frontend/src/components/NotificationPhonePreview.vue +++ b/frontend/src/components/NotificationPhonePreview.vue @@ -73,6 +73,7 @@ const wrapperStyle = computed(() => ({ preview /> { + it('uses the shared Sky popup for every central phone notification', () => { + expect(source).not.toContain("from 'konsta/vue'") + expect(source).toContain( + "import { SkyNotification, SkyProvider } from '@/ui'", + ) + expect(source).toContain(' { + expect(source).toContain('getPhoneApp(props.notification.appId)?.iconImage') + expect(source).toContain('class="phone-notification__icon"') + expect(source).toContain('class="phone-notification__open"') + expect(source).toContain(':aria-label="phone.t(\'Notifications.open\')"') + expect(source).toContain('@keydown.esc.stop="emit(\'close\')"') + }) + + it('supports an upward dismiss gesture without a visible close button', () => { + expect(source).toContain('@pointerdown="beginDismissGesture"') + expect(source).toContain('@pointermove="moveDismissGesture"') + expect(source).toContain('pointerOffset <= -28') + expect(source).toContain('velocityY <= -0.3') + expect(source).not.toContain('button="close"') + expect(source).not.toContain('