feat: add web push and sadhna tracker
This commit is contained in:
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS sadhna_logs;
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Sādhana streak: one row per user per day on which they chanted.
|
||||
--
|
||||
-- A "day complete" (what the streak + the growing-plant animation are
|
||||
-- built on) is the conjunction of two daily disciplines:
|
||||
-- 1. chanting today's target rounds → recorded here (chant_done)
|
||||
-- 2. posting at least one quote today → derived live from the posts
|
||||
-- table (a post whose created_at falls on the same calendar day)
|
||||
-- so this table only needs to capture the chanting half.
|
||||
--
|
||||
-- log_date is a DATE (not a timestamp) so the streak rolls over at UTC
|
||||
-- midnight without any timezone math, matching how the rest of the app
|
||||
-- buckets "today". chant_done is stored explicitly — decided at save time
|
||||
-- as rounds_completed >= the user's target THEN — so later edits to a
|
||||
-- user's daily_chanting_rounds never retroactively rewrite history.
|
||||
CREATE TABLE IF NOT EXISTS sadhna_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
log_date DATE NOT NULL,
|
||||
rounds_completed INTEGER NOT NULL DEFAULT 0,
|
||||
chant_done BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (user_id, log_date),
|
||||
CHECK (rounds_completed >= 0 AND rounds_completed <= 1008)
|
||||
);
|
||||
|
||||
-- The streak/growth computation walks a user's recent days newest-first;
|
||||
-- this serves both that scan and the "today" lookup.
|
||||
CREATE INDEX IF NOT EXISTS sadhna_logs_user_date_idx
|
||||
ON sadhna_logs (user_id, log_date DESC);
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS sadhna_reward_grants;
|
||||
DROP TABLE IF EXISTS sadhna_rewards;
|
||||
@@ -0,0 +1,36 @@
|
||||
-- Sādhana streak rewards.
|
||||
--
|
||||
-- `sadhna_rewards` is the catalog of gift items (one row per reward),
|
||||
-- seeded/synced at BFF startup from the embedded rewards.json so the
|
||||
-- name / meaning / image_url stay in lockstep with the source of truth.
|
||||
--
|
||||
-- `sadhna_reward_grants` records which reward a user earned at which
|
||||
-- streak milestone (a multiple of 7 days). The UNIQUE (user_id, milestone)
|
||||
-- makes granting idempotent — a milestone hands out exactly one gift no
|
||||
-- matter how often the dashboard is recomputed. `celebrated_at` is NULL
|
||||
-- until the client has shown the confetti celebration, so a freshly earned
|
||||
-- gift surfaces exactly once.
|
||||
CREATE TABLE IF NOT EXISTS sadhna_rewards (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
meaning TEXT NOT NULL DEFAULT '',
|
||||
image_url TEXT NOT NULL DEFAULT '',
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sort INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sadhna_reward_grants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
reward_id UUID NOT NULL REFERENCES sadhna_rewards(id) ON DELETE CASCADE,
|
||||
milestone INTEGER NOT NULL,
|
||||
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
celebrated_at TIMESTAMPTZ,
|
||||
UNIQUE (user_id, milestone)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS sadhna_reward_grants_user_idx
|
||||
ON sadhna_reward_grants (user_id, granted_at DESC);
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS push_subscriptions;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- Web Push subscriptions. One row per browser/device push endpoint a user
|
||||
-- has granted. The endpoint is globally unique (it's a per-subscription URL
|
||||
-- at the push service), so re-subscribing the same browser upserts. p256dh
|
||||
-- and auth are the subscription's encryption keys (base64url). Dead
|
||||
-- endpoints (404/410 from the push service) are pruned on send.
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
endpoint TEXT NOT NULL UNIQUE,
|
||||
p256dh TEXT NOT NULL,
|
||||
auth TEXT NOT NULL,
|
||||
user_agent TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
last_used_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS push_subscriptions_user_idx
|
||||
ON push_subscriptions (user_id);
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS last_quote_reminder_on;
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS last_chant_reminder_on;
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS sadhna_reminders_enabled;
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS timezone;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- Sādhana reminder support.
|
||||
--
|
||||
-- timezone: the user's IANA zone (e.g. "Asia/Kolkata"), reported by the
|
||||
-- browser, so reminders fire at their local clock time. Defaults to UTC
|
||||
-- until the client reports a real zone.
|
||||
--
|
||||
-- sadhna_reminders_enabled: per-user opt-out for the daily reminders.
|
||||
--
|
||||
-- last_{chant,quote}_reminder_on: the UTC date a reminder of that kind was
|
||||
-- last sent, so each kind goes out at most once per (UTC) day.
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS timezone TEXT NOT NULL DEFAULT 'UTC';
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS sadhna_reminders_enabled BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_chant_reminder_on DATE;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_quote_reminder_on DATE;
|
||||
Reference in New Issue
Block a user