Add question types, nudge feature, and share modal

- Add new question card components (photo, audio, text, who-likely)
- Add share preview modal for matches
- Add nudge notification system with rate limiting
- Add session-based notification improvements
- Add match confetti celebration animation
- Update onboarding flow with paywall tracking
- Remove deprecated swipe card and tutorial components
- Update GlassCard/GlassInput to use solid backgrounds
- Fix routing issues after packs screen removal
This commit is contained in:
jjbrunton committed 2026-01-25 19:17:39 +00:00
1 parent 8612b16b93
commit 1daf386765
114 files changed
+14877 -7911

No files matched your search

+28 -1
View File
@@ -12,6 +12,10 @@ export interface Profile {
public_key_jwk?: Record<string, unknown> | null;
/** When true, packs marked as is_explicit will be hidden */
hide_nsfw?: boolean;
/** Whether onboarding flow has been completed */
onboarding_completed?: boolean;
/** Version of onboarding flow completed. 0 = never, 1+ = that version */
onboarding_version?: number;
}
// Couple pairing
@@ -45,6 +49,10 @@ export interface Question {
required_props?: string[] | null;
/** References the primary question that this is an inverse of (e.g., give vs receive). NULL means this is a primary/standalone question. */
inverse_of?: string | null;
/** Type of question interaction (default: 'swipe') */
question_type?: QuestionType;
/** Configuration options for specific question types */
config?: QuestionConfig | null;
created_at: string;
deleted_at?: string | null;
}
@@ -52,8 +60,23 @@ export interface Question {
// User's answer type
export type AnswerType = 'yes' | 'no' | 'maybe';
// Question type (default is 'swipe' for backwards compatibility)
export type QuestionType = 'swipe' | 'text_answer' | 'audio' | 'photo' | 'who_likely';
// Question configuration options
export interface QuestionConfig {
max_duration_seconds?: number; // For audio questions (default: 60)
}
// Response data for non-swipe question types
export type ResponseData =
| { type: 'text_answer'; text: string }
| { type: 'audio'; media_path: string; duration_seconds: number }
| { type: 'photo'; media_path: string }
| { type: 'who_likely'; chosen_user_id: string };
// Match type when both partners agree
export type MatchType = 'yes_yes' | 'yes_maybe' | 'maybe_maybe';
export type MatchType = 'yes_yes' | 'yes_maybe' | 'maybe_maybe' | 'both_answered';
// User response to a question
export interface Response {
@@ -62,6 +85,8 @@ export interface Response {
question_id: string;
couple_id: string;
answer: AnswerType;
/** Additional response data for non-swipe question types */
response_data?: ResponseData | null;
created_at: string;
}
@@ -72,6 +97,8 @@ export interface Match {
question_id: string;
match_type: MatchType;
is_new: boolean;
/** Summary of both partners' response data (keyed by user_id) for non-swipe questions */
response_summary?: Record<string, ResponseData> | null;
created_at: string;
}
+68 -2
View File
@@ -609,6 +609,7 @@ export type Database = {
is_new: boolean | null
match_type: Database["public"]["Enums"]["match_type"]
question_id: string
response_summary: Json | null
}
Insert: {
couple_id: string
@@ -617,6 +618,7 @@ export type Database = {
is_new?: boolean | null
match_type: Database["public"]["Enums"]["match_type"]
question_id: string
response_summary?: Json | null
}
Update: {
couple_id?: string
@@ -625,6 +627,7 @@ export type Database = {
is_new?: boolean | null
match_type?: Database["public"]["Enums"]["match_type"]
question_id?: string
response_summary?: Json | null
}
Relationships: [
{
@@ -797,6 +800,53 @@ export type Database = {
},
]
}
notification_preferences: {
Row: {
user_id: string
matches_enabled: boolean | null
messages_enabled: boolean | null
partner_activity_enabled: boolean | null
nudges_enabled: boolean | null
pack_changes_enabled: boolean | null
new_packs_enabled: boolean | null
streak_milestones_enabled: boolean | null
created_at: string | null
updated_at: string | null
}
Insert: {
user_id: string
matches_enabled?: boolean | null
messages_enabled?: boolean | null
partner_activity_enabled?: boolean | null
nudges_enabled?: boolean | null
pack_changes_enabled?: boolean | null
new_packs_enabled?: boolean | null
streak_milestones_enabled?: boolean | null
created_at?: string | null
updated_at?: string | null
}
Update: {
user_id?: string
matches_enabled?: boolean | null
messages_enabled?: boolean | null
partner_activity_enabled?: boolean | null
nudges_enabled?: boolean | null
pack_changes_enabled?: boolean | null
new_packs_enabled?: boolean | null
streak_milestones_enabled?: boolean | null
created_at?: string | null
updated_at?: string | null
}
Relationships: [
{
foreignKeyName: "notification_preferences_user_id_fkey"
columns: ["user_id"]
isOneToOne: true
referencedRelation: "profiles"
referencedColumns: ["id"]
},
]
}
pack_topics: {
Row: {
pack_id: string
@@ -836,6 +886,7 @@ export type Database = {
gender: string | null
id: string
is_premium: boolean | null
last_nudge_sent_at: string | null
max_intensity: number | null
name: string | null
onboarding_completed: boolean | null
@@ -853,6 +904,7 @@ export type Database = {
gender?: string | null
id: string
is_premium?: boolean | null
last_nudge_sent_at?: string | null
max_intensity?: number | null
name?: string | null
onboarding_completed?: boolean | null
@@ -870,6 +922,7 @@ export type Database = {
gender?: string | null
id?: string
is_premium?: boolean | null
last_nudge_sent_at?: string | null
max_intensity?: number | null
name?: string | null
onboarding_completed?: boolean | null
@@ -948,31 +1001,37 @@ export type Database = {
questions: {
Row: {
allowed_couple_genders: string[] | null
config: Json | null
created_at: string | null
id: string
intensity: number | null
pack_id: string
partner_text: string | null
question_type: Database["public"]["Enums"]["question_type"]
target_user_genders: string[] | null
text: string
}
Insert: {
allowed_couple_genders?: string[] | null
config?: Json | null
created_at?: string | null
id?: string
intensity?: number | null
pack_id: string
partner_text?: string | null
question_type?: Database["public"]["Enums"]["question_type"]
target_user_genders?: string[] | null
text: string
}
Update: {
allowed_couple_genders?: string[] | null
config?: Json | null
created_at?: string | null
id?: string
intensity?: number | null
pack_id?: string
partner_text?: string | null
question_type?: Database["public"]["Enums"]["question_type"]
target_user_genders?: string[] | null
text?: string
}
@@ -1032,6 +1091,7 @@ export type Database = {
created_at: string | null
id: string
question_id: string
response_data: Json | null
user_id: string
}
Insert: {
@@ -1040,6 +1100,7 @@ export type Database = {
created_at?: string | null
id?: string
question_id: string
response_data?: Json | null
user_id: string
}
Update: {
@@ -1048,6 +1109,7 @@ export type Database = {
created_at?: string | null
id?: string
question_id?: string
response_data?: Json | null
user_id?: string
}
Relationships: [
@@ -1347,12 +1409,14 @@ export type Database = {
Args: { target_pack_id?: string }
Returns: {
allowed_couple_genders: string[]
config: Json
id: string
intensity: number
is_two_part: boolean
pack_id: string
partner_answered: boolean
partner_text: string
question_type: Database["public"]["Enums"]["question_type"]
target_user_genders: string[]
text: string
}[]
@@ -1424,7 +1488,8 @@ export type Database = {
| "resolved"
| "closed"
feedback_type: "bug" | "feature_request" | "general" | "question"
match_type: "yes_yes" | "yes_maybe" | "maybe_maybe"
match_type: "yes_yes" | "yes_maybe" | "maybe_maybe" | "both_answered"
question_type: "swipe" | "text_answer" | "audio" | "photo" | "who_likely"
report_reason: "harassment" | "spam" | "inappropriate_content" | "other"
subscription_status:
| "active"
@@ -1572,7 +1637,8 @@ export const Constants = {
],
feedback_status: ["new", "reviewed", "in_progress", "resolved", "closed"],
feedback_type: ["bug", "feature_request", "general", "question"],
match_type: ["yes_yes", "yes_maybe", "maybe_maybe"],
match_type: ["yes_yes", "yes_maybe", "maybe_maybe", "both_answered"],
question_type: ["swipe", "text_answer", "audio", "photo", "who_likely"],
report_reason: ["harassment", "spam", "inappropriate_content", "other"],
subscription_status: [
"active",