mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 17:23:25 +00:00
feat(phone): unify popups and add dynamic island
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const source = readFileSync(
|
||||
new URL('./PhoneDynamicIsland.vue', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const appSource = readFileSync(new URL('../App.vue', import.meta.url), 'utf8')
|
||||
const recorderSource = readFileSync(
|
||||
new URL('./PhoneMemoRecorder.vue', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const memosSource = readFileSync(
|
||||
new URL('../views/apps/MemosApp.vue', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
const clockSource = readFileSync(
|
||||
new URL('../views/apps/ClockApp.vue', import.meta.url),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
describe('Phone Dynamic Island contract', () => {
|
||||
it('renders once at phone shell level instead of forcing calls into Phone', () => {
|
||||
expect(appSource).toContain(
|
||||
"import PhoneDynamicIsland from '@/components/PhoneDynamicIsland.vue'",
|
||||
)
|
||||
expect(appSource).toContain('<PhoneDynamicIsland v-if="!setupRequired" />')
|
||||
expect(appSource).toContain(
|
||||
'phone.isOpen || notifications.current || calls.activeCall',
|
||||
)
|
||||
expect(appSource).not.toContain(
|
||||
"window.setTimeout(() => void router.push('/apps/phone'), 0)",
|
||||
)
|
||||
})
|
||||
|
||||
it('prioritizes calls and exposes answer, decline, and hangup controls', () => {
|
||||
expect(source.indexOf("return 'incoming-call'")).toBeLessThan(
|
||||
source.indexOf("return 'recording'"),
|
||||
)
|
||||
expect(source).toContain('@click.stop="calls.answer()"')
|
||||
expect(source).toContain('@click.stop="endCall"')
|
||||
expect(source).toContain('void calls.decline()')
|
||||
expect(source).toContain('void calls.hangup()')
|
||||
expect(source).toContain('call.answeredAt ?? call.startedAt')
|
||||
})
|
||||
|
||||
it('connects music, recorder, timer, and stopwatch controls to their stores', () => {
|
||||
expect(source).toContain('@click.stop="music.previous()"')
|
||||
expect(source).toContain('@click.stop="music.toggle()"')
|
||||
expect(source).toContain('@click.stop="music.next()"')
|
||||
expect(source).toContain("postRecorderCommand('memo:recordStop')")
|
||||
expect(source).toContain('clock.pauseTimer(Date.now())')
|
||||
expect(source).toContain('clock.pauseStopwatch(Date.now())')
|
||||
expect(source).toContain('clock.addLap(Date.now())')
|
||||
})
|
||||
|
||||
it('keeps recorder state available across app changes', () => {
|
||||
expect(recorderSource).toContain(
|
||||
"message.type === 'memo:recordStateRequest'",
|
||||
)
|
||||
expect(memosSource).toContain(
|
||||
"postRecorderCommand('memo:recordStateRequest')",
|
||||
)
|
||||
expect(memosSource).not.toContain(
|
||||
"if (recordingActive.value) postRecorderCommand('memo:recordCancel')",
|
||||
)
|
||||
})
|
||||
|
||||
it('opens both clock live activities on the correct clock tab', () => {
|
||||
expect(source).toContain("'/apps/clock?section=timer'")
|
||||
expect(source).toContain("'/apps/clock?section=stopwatch'")
|
||||
expect(clockSource).toContain(
|
||||
"section === 'timer' || section === 'stopwatch'",
|
||||
)
|
||||
})
|
||||
|
||||
it('animates state changes and moves popup notifications below expanded UI', () => {
|
||||
expect(source).toContain('<Transition name="phone-dynamic-island">')
|
||||
expect(source).toContain(
|
||||
'<Transition name="phone-dynamic-island-content" mode="out-in">',
|
||||
)
|
||||
expect(source).toContain(".phone-dynamic-island[data-expanded='true']")
|
||||
expect(source).toContain('~ .phone-notification-provider')
|
||||
expect(source).toContain('@media (prefers-reduced-motion: reduce)')
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -557,6 +557,8 @@ function onMessage(event: MessageEvent): void {
|
||||
void stopRecording(data)
|
||||
} else if (message.type === 'memo:recordCancel') {
|
||||
cancelRecording()
|
||||
} else if (message.type === 'memo:recordStateRequest') {
|
||||
postRecorderState(currentState)
|
||||
} else if (message.type === 'memos:uploadReady') {
|
||||
void uploadReady(data as MemoUploadReady)
|
||||
} else if (message.type === 'memos:uploadResult') {
|
||||
|
||||
Reference in New Issue
Block a user