Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions app/composables/useDialogManagerState.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
export type DialogManagerDialog = 'premium' | 'installPwa' | 'feedback' | 'newsletter' | 'review'
type AutomaticDialog = Exclude<DialogManagerDialog, 'premium'>

export function resolvePendingDialog(
isPremiumDialogOpen: boolean,
automaticDialog: AutomaticDialog | undefined,
hasConsumedAutomaticDialog: boolean
): DialogManagerDialog | undefined {
if (isPremiumDialogOpen) {
return 'premium'
}

return hasConsumedAutomaticDialog ? undefined : automaticDialog
}

export function useDialogManagerState() {
const { timesTheAppHasBeenOpened, promptInstallPwa, promptFeedback, promptNewsletter, promptReview } =
Expand All @@ -12,11 +25,8 @@ export function useDialogManagerState() {
})
}

const pendingDialog = computed<DialogManagerDialog | undefined>(() => {
if (isPremiumDialogOpen.value) {
return 'premium'
}

const hasConsumedAutomaticDialog = useState('dialog-manager-has-consumed-automatic', () => false)
const automaticDialog = computed<AutomaticDialog | undefined>(() => {
if (timesTheAppHasBeenOpened.value >= 3 && !promptInstallPwa.value && !isStandaloneDisplayMode.value) {
return 'installPwa'
}
Expand All @@ -33,6 +43,9 @@ export function useDialogManagerState() {
return 'review'
}
})
const pendingDialog = computed(() =>
resolvePendingDialog(isPremiumDialogOpen.value, automaticDialog.value, hasConsumedAutomaticDialog.value)
)

function closeDialog(dialog = pendingDialog.value) {
switch (dialog) {
Expand All @@ -42,18 +55,22 @@ export function useDialogManagerState() {

case 'installPwa':
promptInstallPwa.value = true
hasConsumedAutomaticDialog.value = true
break

case 'feedback':
promptFeedback.value = true
hasConsumedAutomaticDialog.value = true
break

case 'newsletter':
promptNewsletter.value = true
hasConsumedAutomaticDialog.value = true
break

case 'review':
promptReview.value = true
hasConsumedAutomaticDialog.value = true
break
}
}
Expand Down
88 changes: 88 additions & 0 deletions test/assets/dialog-manager-state.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { resolvePendingDialog, useDialogManagerState } from '../../app/composables/useDialogManagerState'

type State<T> = { value: T }

const states = new Map<string, State<unknown>>()
const statistics = {
timesTheAppHasBeenOpened: { value: 30 },
promptInstallPwa: { value: false },
promptFeedback: { value: false },
promptNewsletter: { value: false },
promptReview: { value: false }
}
const premium = { value: false }

beforeEach(() => {
states.clear()
statistics.timesTheAppHasBeenOpened.value = 30
statistics.promptInstallPwa.value = false
statistics.promptFeedback.value = false
statistics.promptNewsletter.value = false
statistics.promptReview.value = false
premium.value = false

vi.stubGlobal('computed', <T>(getter: () => T) => ({
get value() {
return getter()
}
}))
vi.stubGlobal('useState', <T>(key: string, init: () => T) => {
if (!states.has(key)) {
states.set(key, { value: init() })
}

return states.get(key) as State<T>
})
vi.stubGlobal('useAppStatistics', () => statistics)
vi.stubGlobal('usePremiumDialog', () => ({ open: premium }))
})

afterEach(() => {
vi.unstubAllGlobals()
})

describe('resolvePendingDialog', () => {
it('does not select another automatic dialog after one has been shown', () => {
expect(resolvePendingDialog(false, 'feedback', true)).toBeUndefined()
})

it('allows premium after an automatic dialog has been shown', () => {
expect(resolvePendingDialog(true, 'feedback', true)).toBe('premium')
})
})

describe('useDialogManagerState', () => {
it('does not cascade or reset automatic dialogs when recreated during one startup', () => {
const firstManager = useDialogManagerState()

expect(firstManager.pendingDialog.value).toBe('installPwa')
firstManager.closeDialog()
expect(firstManager.pendingDialog.value).toBeUndefined()

const recreatedManager = useDialogManagerState()

expect(recreatedManager.pendingDialog.value).toBeUndefined()
})

it('lets premium preempt and restore an unconsumed automatic dialog', () => {
const manager = useDialogManagerState()

premium.value = true
expect(manager.pendingDialog.value).toBe('premium')

manager.closeDialog()
expect(manager.pendingDialog.value).toBe('installPwa')
})

it('lets premium open after an automatic dialog has been consumed', () => {
const manager = useDialogManagerState()

manager.closeDialog()
premium.value = true
expect(manager.pendingDialog.value).toBe('premium')

manager.closeDialog()
expect(manager.pendingDialog.value).toBeUndefined()
})
})