Files
donetick/src/utils/Onboarding.js
Mohamad Tarbin d641710a41 Reshape onboarding v1.3 (#185)
* Initialize Onboarding Flow

* Shorten Onboarding

* Add Setup circle  and what are the current way to add tasks to the onboarding.
update donetick logo

* improve wording

* Skip onboarding flow for now

* Enable onboarding flow
2026-08-02 20:53:28 -04:00

115 lines
3.2 KiB
JavaScript

import { Capacitor } from '@capacitor/core'
// First-run onboarding is a native-app-only flow. On the web the user already
// chose to visit a URL, so we drop them straight on the auth screens.
const STORAGE_KEY = 'onboardingCompletedAt'
export const isNativeApp = () => {
try {
return Capacitor.isNativePlatform()
} catch {
return false
}
}
export const hasSeenOnboarding = () => {
try {
return Boolean(localStorage.getItem(STORAGE_KEY))
} catch {
// Private-mode / storage-disabled webviews: never trap the user in a loop.
return true
}
}
export const markOnboardingSeen = () => {
try {
localStorage.setItem(STORAGE_KEY, new Date().toISOString())
} catch {
// ignore, worst case the flow is shown once more
}
}
export const resetOnboarding = () => {
try {
localStorage.removeItem(STORAGE_KEY)
} catch {
// ignore
}
}
const ACQUISITION_SOURCE_KEY = 'acquisitionSource'
/**
* Stashes the "where'd you hear about us" answer locally for now. No
* analytics pipeline is wired up yet — this is the single place that'll
* change once there is one, so the survey screen itself never has to.
*/
export const recordAcquisitionSource = source => {
try {
localStorage.setItem(ACQUISITION_SOURCE_KEY, source)
} catch {
// ignore, this is best-effort telemetry
}
}
const PRIVACY_PREFERENCES_KEY = 'privacyPreferences'
/**
* Stashes the self-hosted privacy opt-ins locally, same stub-for-now
* treatment as recordAcquisitionSource: no crash reporter or PostHog is wired
* up yet, so this is just the one place that'll change once there is one.
*/
export const recordPrivacyPreferences = ({ crashReports, analytics }) => {
try {
localStorage.setItem(
PRIVACY_PREFERENCES_KEY,
JSON.stringify({ crashReports, analytics }),
)
} catch {
// ignore, this is best-effort telemetry
}
}
/**
* Asks the OS for notification permission during onboarding and records the
* answer under the same `notificationPreferences` key NotificationAccessSnackbar
* reads, so a user who says yes here is never asked again after signing in.
*
* Only the permission is requested: registering the push token needs a session,
* and there isn't one yet. The snackbar picks that up once the user is in.
*/
export const requestNotificationPermission = async () => {
if (!isNativeApp()) return false
try {
const { LocalNotifications } = await import(
'@capacitor/local-notifications'
)
const { Preferences } = await import('@capacitor/preferences')
const result = await LocalNotifications.requestPermissions()
const granted = result?.display === 'granted'
await Preferences.set({
key: 'notificationPreferences',
value: JSON.stringify({ optOut: false, granted }),
})
return granted
} catch {
// Permission plugins are missing or the prompt was dismissed: carry on,
// the in-app snackbar can still ask later.
return false
}
}
export const haptic = async (kind = 'light') => {
if (!isNativeApp()) return
try {
const { Haptics, ImpactStyle } = await import('@capacitor/haptics')
await Haptics.impact({
style: kind === 'medium' ? ImpactStyle.Medium : ImpactStyle.Light,
})
} catch {
// no haptics on this platform
}
}