feat: Add initial frontend components and their corresponding build assets, along with generated API types and configuration.
Some checks failed
Build and Release / build-and-release (push) Failing after 7m50s

This commit is contained in:
Bryan1029384756
2026-02-11 06:24:33 -06:00
parent cb4361da1a
commit c472f0ee2d
369 changed files with 1423 additions and 395 deletions

113
convex/readState.ts Normal file
View File

@@ -0,0 +1,113 @@
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
// Get read state for a single channel
export const getReadState = query({
args: {
userId: v.id("userProfiles"),
channelId: v.id("channels"),
},
returns: v.union(
v.object({
lastReadTimestamp: v.number(),
}),
v.null()
),
handler: async (ctx, args) => {
const state = await ctx.db
.query("channelReadState")
.withIndex("by_user_and_channel", (q) =>
q.eq("userId", args.userId).eq("channelId", args.channelId)
)
.unique();
if (!state) return null;
return { lastReadTimestamp: state.lastReadTimestamp };
},
});
// Mark a channel as read up to a given timestamp
export const markRead = mutation({
args: {
userId: v.id("userProfiles"),
channelId: v.id("channels"),
lastReadTimestamp: v.number(),
},
returns: v.null(),
handler: async (ctx, args) => {
const existing = await ctx.db
.query("channelReadState")
.withIndex("by_user_and_channel", (q) =>
q.eq("userId", args.userId).eq("channelId", args.channelId)
)
.unique();
if (existing) {
// Only update if timestamp is newer
if (args.lastReadTimestamp > existing.lastReadTimestamp) {
await ctx.db.patch(existing._id, {
lastReadTimestamp: args.lastReadTimestamp,
});
}
} else {
await ctx.db.insert("channelReadState", {
userId: args.userId,
channelId: args.channelId,
lastReadTimestamp: args.lastReadTimestamp,
});
}
return null;
},
});
// Get all read states for a user (used by Sidebar)
export const getAllReadStates = query({
args: {
userId: v.id("userProfiles"),
},
returns: v.array(
v.object({
channelId: v.id("channels"),
lastReadTimestamp: v.number(),
})
),
handler: async (ctx, args) => {
const states = await ctx.db
.query("channelReadState")
.withIndex("by_user", (q) => q.eq("userId", args.userId))
.collect();
return states.map((s) => ({
channelId: s.channelId,
lastReadTimestamp: s.lastReadTimestamp,
}));
},
});
// Get the latest message timestamp per channel (used by Sidebar)
export const getLatestMessageTimestamps = query({
args: {
channelIds: v.array(v.id("channels")),
},
returns: v.array(
v.object({
channelId: v.id("channels"),
latestTimestamp: v.number(),
})
),
handler: async (ctx, args) => {
const results = [];
for (const channelId of args.channelIds) {
const latestMsg = await ctx.db
.query("messages")
.withIndex("by_channel", (q) => q.eq("channelId", channelId))
.order("desc")
.first();
if (latestMsg) {
results.push({
channelId,
latestTimestamp: latestMsg._creationTime,
});
}
}
return results;
},
});