mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
feat(onboarding): skippable avatar with first-match prompt and usage-reason pack picks
Lets new users skip the onboarding avatar step and instead defers the ask to a lightweight, dismissible prompt shown once after the couple's first match. Also personalizes the home screen's first pack recommendation using the usage_reason already collected during onboarding, which now round-trips through GET /v1/me. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f22d4a3649
commit
fd8981fa28
14 files changed
+567
-77
No files matched your search
@@ -257,6 +257,10 @@ export const Events = {
|
||||
// Profile events
|
||||
profileUpdated: (fields: string[]) => logEvent("profile_updated", { fields: fields.join(",") }),
|
||||
avatarUploaded: () => logEvent("avatar_uploaded"),
|
||||
avatarSkipped: () => logEvent("avatar_skipped"),
|
||||
avatarPromptShown: (source: string) => logEvent("avatar_prompt_shown", { source }),
|
||||
avatarPromptAccepted: (source: string) => logEvent("avatar_prompt_accepted", { source }),
|
||||
avatarPromptDismissed: (source: string) => logEvent("avatar_prompt_dismissed", { source }),
|
||||
|
||||
// Notification events
|
||||
notificationPermissionGranted: () => logEvent("notification_permission_granted"),
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
const STORAGE_KEY_PREFIX = "avatar_prompt_seen";
|
||||
|
||||
const buildKey = (userId: string): string => `${STORAGE_KEY_PREFIX}_${userId}`;
|
||||
|
||||
async function getStorage(key: string): Promise<boolean> {
|
||||
try {
|
||||
let data: string | null = null;
|
||||
|
||||
if (Platform.OS === "web") {
|
||||
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
||||
data = window.localStorage.getItem(key);
|
||||
}
|
||||
} else {
|
||||
data = await SecureStore.getItemAsync(key);
|
||||
}
|
||||
|
||||
return data === "true";
|
||||
} catch (error) {
|
||||
console.error("Error reading avatar prompt state:", error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function setStorage(key: string, seen: boolean): Promise<void> {
|
||||
try {
|
||||
const data = seen ? "true" : "false";
|
||||
|
||||
if (Platform.OS === "web") {
|
||||
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
||||
window.localStorage.setItem(key, data);
|
||||
}
|
||||
} else {
|
||||
await SecureStore.setItemAsync(key, data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving avatar prompt state:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this user has already been shown the deferred avatar prompt (at the
|
||||
* first-match moment), regardless of whether they added a photo or dismissed it.
|
||||
* The prompt is shown at most once per user.
|
||||
*/
|
||||
export async function hasSeenAvatarPrompt(userId: string): Promise<boolean> {
|
||||
return getStorage(buildKey(userId));
|
||||
}
|
||||
|
||||
export async function markAvatarPromptSeen(userId: string): Promise<void> {
|
||||
await setStorage(buildKey(userId), true);
|
||||
}
|
||||
Reference in new issue
Block a user