Skip to content

Improve chat reliability and Supabase migration hardening#62

Open
0x3EF8 wants to merge 1 commit intohallofcodes:masterfrom
0x3EF8:pr/local-sync
Open

Improve chat reliability and Supabase migration hardening#62
0x3EF8 wants to merge 1 commit intohallofcodes:masterfrom
0x3EF8:pr/local-sync

Conversation

@0x3EF8
Copy link
Copy Markdown
Member

@0x3EF8 0x3EF8 commented Apr 7, 2026

Summary

  • Improve chat realtime delivery for direct and global chat with broadcast fast-path plus database reconciliation
  • Prevent duplicate attachment sends by adding an in-flight send lock and disabling send actions while sending
  • Improve unread count reliability by recalculating counts from server read state
  • Harden direct-message creation flow and error handling for conversation participant operations
  • Refresh Supabase fresh setup with a consolidated baseline migration and tracked migrations archive
  • Include conversation participant RLS and secure trigger hardening in the fresh baseline migration
  • Update migration setup documentation for clean project bootstrap

Validation

  • npm run lint
  • npm run build

Copilot AI review requested due to automatic review settings April 7, 2026 11:08
@0x3EF8 0x3EF8 changed the title Sync fork snapshot and harden chat + Supabase migration setup Improve chat reliability and Supabase migration hardening Apr 7, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR syncs the fork snapshot onto an upstream-compatible branch while tightening the Supabase baseline migration flow and improving chat realtime UX (dedupe/optimistic messaging, unread count accuracy, and safer conversation creation).

Changes:

  • Introduces a squashed Supabase baseline migration for fresh installs and archives historical migrations.
  • Improves chat realtime behavior (broadcast-based optimistic delivery, duplicate-send prevention, unread count refresh, user picker sourcing).
  • Updates project docs and regenerated Supabase TypeScript types; includes a small dependency lockfile bump.

Reviewed changes

Copilot reviewed 32 out of 43 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
supabase/migrations/20260407120000_baseline_fresh_setup.sql New consolidated baseline migration including chat tables/RLS, triggers, and archived migration content.
supabase/migrations/*.sql (removed) Removes individual historical migrations now represented in the baseline.
supabase/migrations_archive/*.sql Adds archived copies of historical migrations for reference/inspection.
README.md Documents the new baseline + archive migration workflow and setup steps.
.gitignore Un-ignores supabase/migrations_archive so archived migrations are tracked.
.env.example Updates example Supabase project ID value.
app/supabase-types.ts Regenerates types to match updated schema.
app/components/Chat.tsx Integrates new composer/stream behavior and memoizes message filtering.
app/components/chat/hooks/useChatUserPicker.ts Switches user picker source to global conversation participants.
app/components/chat/hooks/useChatMessageComposer.ts Adds send dedupe, sending state, and broadcast optimistic messages + retract.
app/components/chat/hooks/useChatInputBehavior.ts Blocks Enter-to-send while a send is in-flight.
app/components/chat/hooks/useChatConversationsRealtime.ts Refreshes unread counts via server-derived counts instead of local increments.
app/components/chat/hooks/useChatConversationActions.ts Hardens conversation creation (explicit IDs, better rollback + error surfacing).
app/components/chat/hooks/useActiveConversationStream.ts Adds broadcast message handling and reconciles ephemeral vs persisted messages.
app/components/dashboard/Navbar.tsx Changes unread refresh strategy and sidebar collapsed defaults.
package-lock.json Updates a few transitive dependency versions/integrities.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

set email = excluded.email;
return new;
end;
$$ language plpgsql security definer;
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public.handle_new_user() is declared SECURITY DEFINER but does not set an explicit search_path. This is a known Postgres footgun because a caller-controlled search_path can change which objects are resolved inside the function. Set SET search_path = public (optionally including pg_temp) on the function definition to harden it.

Suggested change
$$ language plpgsql security definer;
$$ language plpgsql security definer
set search_path = public;

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +47
CREATE OR REPLACE VIEW top_user_stats AS
SELECT
us.user_id,
us.total_seconds,
a.email
FROM user_stats us
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The top_user_stats view is created without a schema qualifier (CREATE OR REPLACE VIEW top_user_stats). In migrations this can end up in an unexpected schema if search_path differs, and it also makes dependencies less explicit. Prefer CREATE OR REPLACE VIEW public.top_user_stats ... (and schema-qualify referenced tables consistently).

Suggested change
CREATE OR REPLACE VIEW top_user_stats AS
SELECT
us.user_id,
us.total_seconds,
a.email
FROM user_stats us
CREATE OR REPLACE VIEW public.top_user_stats AS
SELECT
us.user_id,
us.total_seconds,
a.email
FROM public.user_stats us

Copilot uses AI. Check for mistakes.
@@ -1,4 +1,4 @@
export type Json =
export type Json =
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated types file appears to start with a UTF-8 BOM (there is an invisible character before export type Json). This can cause linting/formatting or tooling issues in some environments. Re-save/regenerate the file without the BOM so the first character is e in export.

Suggested change
export type Json =
export type Json =

Copilot uses AI. Check for mistakes.

SUPABASE_ACCESS_TOKEN=
SUPABASE_PROJECT_ID=
SUPABASE_PROJECT_ID=vswabkwgipyweqsabzwv
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.example should not contain a real Supabase project ref; it’s meant to be a template. Replace the concrete value with an empty value or obvious placeholder (e.g. <project-id>), so users don’t accidentally link/push migrations to the wrong project.

Suggested change
SUPABASE_PROJECT_ID=vswabkwgipyweqsabzwv
SUPABASE_PROJECT_ID=<project-id>

Copilot uses AI. Check for mistakes.
setMobileHidden(true);
} else {
setCollapsed(false);
setCollapsed(true);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On desktop resize, the else branch now forces collapsed to true, which makes the sidebar start collapsed and also re-collapse whenever the window resizes. If the intended behavior is to show the expanded sidebar on desktop (as before), this should set collapsed to false in the non-mobile branch.

Suggested change
setCollapsed(true);
setCollapsed(false);

Copilot uses AI. Check for mistakes.
Comment on lines +395 to +402
CREATE OR REPLACE VIEW top_user_stats AS
SELECT
us.user_id,
us.total_seconds,
a.email,
us.categories -- new column
FROM user_stats us
JOIN auth.users a ON us.user_id = a.id;
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This second CREATE OR REPLACE VIEW top_user_stats statement is also missing an explicit schema. Qualify it as public.top_user_stats here as well so the baseline migration is deterministic regardless of search_path.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +85
values (new.id, new.email)
on conflict (id) do update
set email = excluded.email;
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public.profiles.email is defined NOT NULL, but handle_new_user() inserts NEW.email directly. Supabase auth.users.email can be NULL (e.g., phone-based signups), which would make this trigger fail and block user creation. Consider using a fallback (like the @user.local pattern used elsewhere) or relaxing the profiles.email constraint if email-less accounts are supported.

Suggested change
values (new.id, new.email)
on conflict (id) do update
set email = excluded.email;
values (new.id, coalesce(new.email, new.id::text || '@user.local'))
on conflict (id) do update
set email = coalesce(excluded.email, public.profiles.id::text || '@user.local');

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants