-- Notifications Migration for BroadcastBeat -- Tracks thread replies, mentions, upvotes, and follow activity -- ============================================================ -- 1. NOTIFICATIONS TABLE -- ============================================================ CREATE TABLE IF NOT EXISTS public.notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE, type TEXT NOT NULL CHECK (type IN ('thread_reply', 'mention', 'upvote', 'follow')), title TEXT NOT NULL, body TEXT, link TEXT, actor_id UUID REFERENCES public.user_profiles(id) ON DELETE SET NULL, actor_name TEXT, is_read BOOLEAN DEFAULT false, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON public.notifications(user_id); CREATE INDEX IF NOT EXISTS idx_notifications_user_unread ON public.notifications(user_id, is_read) WHERE is_read = false; CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON public.notifications(created_at DESC); -- ============================================================ -- 2. RLS POLICIES -- ============================================================ ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can view own notifications" ON public.notifications; CREATE POLICY "Users can view own notifications" ON public.notifications FOR SELECT USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can update own notifications" ON public.notifications; CREATE POLICY "Users can update own notifications" ON public.notifications FOR UPDATE USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Users can delete own notifications" ON public.notifications; CREATE POLICY "Users can delete own notifications" ON public.notifications FOR DELETE USING (auth.uid() = user_id); DROP POLICY IF EXISTS "Service role can insert notifications" ON public.notifications; CREATE POLICY "Service role can insert notifications" ON public.notifications FOR INSERT WITH CHECK (true); -- ============================================================ -- 3. AUTO-NOTIFY ON FORUM REPLY -- ============================================================ CREATE OR REPLACE FUNCTION public.notify_thread_reply() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_thread_author UUID; v_thread_title TEXT; v_thread_id UUID; BEGIN SELECT author_id, title, id INTO v_thread_author, v_thread_title, v_thread_id FROM public.forum_threads WHERE id = NEW.thread_id; -- Notify thread author (if not replying to own thread and not AI) IF v_thread_author IS NOT NULL AND v_thread_author <> NEW.author_id AND NEW.is_ai_response = false THEN INSERT INTO public.notifications (user_id, type, title, body, link, actor_id, actor_name) VALUES ( v_thread_author, 'thread_reply', 'New reply to your thread', v_thread_title, '/forum/thread/' || v_thread_id::TEXT, NEW.author_id, NEW.author_name ); END IF; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_notify_thread_reply ON public.forum_replies; CREATE TRIGGER trg_notify_thread_reply AFTER INSERT ON public.forum_replies FOR EACH ROW EXECUTE FUNCTION public.notify_thread_reply(); -- ============================================================ -- 4. AUTO-NOTIFY ON UPVOTE -- ============================================================ CREATE OR REPLACE FUNCTION public.notify_upvote() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_target_author UUID; v_target_title TEXT; v_link TEXT; v_voter_name TEXT; BEGIN -- Only notify on upvotes (vote_value = 1) IF NEW.vote_value <> 1 THEN RETURN NEW; END IF; SELECT username INTO v_voter_name FROM public.user_profiles WHERE id = NEW.user_id; IF NEW.target_type = 'thread' THEN SELECT author_id, title INTO v_target_author, v_target_title FROM public.forum_threads WHERE id = NEW.target_id; v_link := '/forum/thread/' || NEW.target_id::TEXT; ELSE SELECT fr.author_id, ft.title INTO v_target_author, v_target_title FROM public.forum_replies fr JOIN public.forum_threads ft ON ft.id = fr.thread_id WHERE fr.id = NEW.target_id; v_link := '/forum/thread/' || ( SELECT thread_id FROM public.forum_replies WHERE id = NEW.target_id )::TEXT; END IF; IF v_target_author IS NOT NULL AND v_target_author <> NEW.user_id THEN INSERT INTO public.notifications (user_id, type, title, body, link, actor_id, actor_name) VALUES ( v_target_author, 'upvote', 'Someone upvoted your ' || NEW.target_type, v_target_title, v_link, NEW.user_id, COALESCE(v_voter_name, 'A member') ); END IF; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_notify_upvote ON public.forum_votes; CREATE TRIGGER trg_notify_upvote AFTER INSERT ON public.forum_votes FOR EACH ROW EXECUTE FUNCTION public.notify_upvote();