mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 10:19:43 +00:00
189 lines
6.2 KiB
PL/PgSQL
189 lines
6.2 KiB
PL/PgSQL
-- Add gentle streaks system
|
|
-- Tracks consecutive days both partners answered questions
|
|
-- Celebrates milestones without guilt-tripping on breaks
|
|
|
|
-- Create couple_streaks table
|
|
CREATE TABLE couple_streaks (
|
|
couple_id UUID PRIMARY KEY REFERENCES couples(id) ON DELETE CASCADE,
|
|
current_streak INTEGER DEFAULT 0,
|
|
longest_streak INTEGER DEFAULT 0,
|
|
last_active_date DATE,
|
|
user1_answered_today BOOLEAN DEFAULT false,
|
|
user2_answered_today BOOLEAN DEFAULT false,
|
|
streak_celebrated_at INTEGER DEFAULT 0, -- Last milestone that was celebrated
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
-- Enable RLS
|
|
ALTER TABLE couple_streaks ENABLE ROW LEVEL SECURITY;
|
|
-- RLS policies
|
|
CREATE POLICY "Users can view their couple's streaks"
|
|
ON couple_streaks FOR SELECT
|
|
USING (couple_id = get_auth_user_couple_id());
|
|
CREATE POLICY "Super admins can view all streaks"
|
|
ON couple_streaks FOR SELECT
|
|
USING (is_super_admin());
|
|
-- Updated_at trigger
|
|
CREATE TRIGGER update_couple_streaks_updated_at
|
|
BEFORE UPDATE ON couple_streaks
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
-- Function to update streak when a response is submitted
|
|
CREATE OR REPLACE FUNCTION update_couple_streak()
|
|
RETURNS TRIGGER
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = 'public'
|
|
AS $$
|
|
DECLARE
|
|
v_user_id UUID;
|
|
v_couple_id UUID;
|
|
v_partner_id UUID;
|
|
v_today DATE;
|
|
v_streak RECORD;
|
|
v_user_is_user1 BOOLEAN;
|
|
BEGIN
|
|
-- Get the user making the response
|
|
v_user_id := auth.uid();
|
|
|
|
-- Skip if no auth context
|
|
IF v_user_id IS NULL THEN
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
-- Get user's couple_id
|
|
SELECT couple_id INTO v_couple_id
|
|
FROM profiles
|
|
WHERE id = v_user_id;
|
|
|
|
-- Skip if user is not in a couple
|
|
IF v_couple_id IS NULL THEN
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
v_today := CURRENT_DATE;
|
|
|
|
-- Get or create streak record
|
|
INSERT INTO couple_streaks (couple_id, last_active_date)
|
|
VALUES (v_couple_id, v_today)
|
|
ON CONFLICT (couple_id) DO NOTHING;
|
|
|
|
SELECT * INTO v_streak
|
|
FROM couple_streaks
|
|
WHERE couple_id = v_couple_id
|
|
FOR UPDATE;
|
|
|
|
-- Determine if this user is "user1" or "user2"
|
|
-- We use a deterministic method: compare UUIDs
|
|
SELECT id INTO v_partner_id
|
|
FROM profiles
|
|
WHERE couple_id = v_couple_id AND id != v_user_id;
|
|
|
|
-- User with smaller UUID is user1
|
|
v_user_is_user1 := v_user_id < COALESCE(v_partner_id, v_user_id);
|
|
|
|
-- Check if streak needs to be reset (missed a day)
|
|
IF v_streak.last_active_date IS NOT NULL AND v_streak.last_active_date < v_today - 1 THEN
|
|
-- Streak is broken - silently reset (no guilt-tripping!)
|
|
UPDATE couple_streaks
|
|
SET
|
|
current_streak = 0,
|
|
user1_answered_today = CASE WHEN v_user_is_user1 THEN true ELSE false END,
|
|
user2_answered_today = CASE WHEN v_user_is_user1 THEN false ELSE true END,
|
|
last_active_date = v_today
|
|
WHERE couple_id = v_couple_id;
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
-- Check if this is a new day
|
|
IF v_streak.last_active_date IS NULL OR v_streak.last_active_date < v_today THEN
|
|
-- New day: reset daily flags, keep streak
|
|
UPDATE couple_streaks
|
|
SET
|
|
user1_answered_today = CASE WHEN v_user_is_user1 THEN true ELSE false END,
|
|
user2_answered_today = CASE WHEN v_user_is_user1 THEN false ELSE true END,
|
|
last_active_date = v_today
|
|
WHERE couple_id = v_couple_id;
|
|
ELSE
|
|
-- Same day: mark this user as having answered
|
|
IF v_user_is_user1 THEN
|
|
UPDATE couple_streaks
|
|
SET user1_answered_today = true
|
|
WHERE couple_id = v_couple_id;
|
|
ELSE
|
|
UPDATE couple_streaks
|
|
SET user2_answered_today = true
|
|
WHERE couple_id = v_couple_id;
|
|
END IF;
|
|
END IF;
|
|
|
|
-- Check if BOTH partners have now answered today
|
|
SELECT * INTO v_streak
|
|
FROM couple_streaks
|
|
WHERE couple_id = v_couple_id;
|
|
|
|
IF v_streak.user1_answered_today AND v_streak.user2_answered_today THEN
|
|
-- Both answered! Increment streak if not already counted for today
|
|
IF v_streak.last_active_date = v_today AND v_streak.current_streak = 0 THEN
|
|
-- First time both answered on a new streak
|
|
UPDATE couple_streaks
|
|
SET
|
|
current_streak = 1,
|
|
longest_streak = GREATEST(longest_streak, 1)
|
|
WHERE couple_id = v_couple_id;
|
|
ELSIF v_streak.last_active_date = v_today THEN
|
|
-- Already counted for today, nothing to do
|
|
NULL;
|
|
ELSE
|
|
-- Continuing streak from previous day
|
|
UPDATE couple_streaks
|
|
SET
|
|
current_streak = current_streak + 1,
|
|
longest_streak = GREATEST(longest_streak, current_streak + 1)
|
|
WHERE couple_id = v_couple_id;
|
|
END IF;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
-- Create trigger on responses table
|
|
CREATE TRIGGER on_response_update_streak
|
|
AFTER INSERT OR UPDATE OF answer ON responses
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_couple_streak();
|
|
-- Function to reset daily flags (called by cron at UTC midnight)
|
|
CREATE OR REPLACE FUNCTION reset_daily_streak_flags()
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = 'public'
|
|
AS $$
|
|
BEGIN
|
|
UPDATE couple_streaks
|
|
SET
|
|
user1_answered_today = false,
|
|
user2_answered_today = false
|
|
WHERE last_active_date < CURRENT_DATE;
|
|
END;
|
|
$$;
|
|
-- Schedule cron job for daily flag reset (runs at 00:00 UTC)
|
|
SELECT cron.schedule(
|
|
'reset-daily-streak-flags',
|
|
'0 0 * * *',
|
|
$$
|
|
SELECT reset_daily_streak_flags();
|
|
$$
|
|
);
|
|
-- Schedule cron job for checking streak milestones (runs at 00:05 UTC)
|
|
SELECT cron.schedule(
|
|
'check-streak-milestones',
|
|
'5 0 * * *',
|
|
$$
|
|
SELECT net.http_post(
|
|
url := 'https://ckjcrkjpmhqhiucifukx.supabase.co/functions/v1/check-streak-milestones',
|
|
headers := jsonb_build_object('Content-Type', 'application/json'),
|
|
body := '{}'::jsonb
|
|
)
|
|
$$
|
|
);
|