Profiles page is donegit add -A!
This commit is contained in:
202
convex/statuses.ts
Normal file
202
convex/statuses.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { ConvexError, v } from 'convex/values';
|
||||
import { getAuthUserId } from '@convex-dev/auth/server';
|
||||
import { mutation, query } from './_generated/server';
|
||||
import type { Doc, Id } from './_generated/dataModel';
|
||||
import { paginationOptsValidator } from 'convex/server';
|
||||
|
||||
// NEW: import ctx and data model types
|
||||
import type { MutationCtx, QueryCtx } from './_generated/server';
|
||||
|
||||
// NEW: shared ctx type for helpers
|
||||
type RWCtx = MutationCtx | QueryCtx;
|
||||
|
||||
// CHANGED: typed helpers
|
||||
const ensureUser = async (
|
||||
ctx: RWCtx,
|
||||
userId: Id<'users'>,
|
||||
) => {
|
||||
const user = await ctx.db.get(userId);
|
||||
if (!user) throw new ConvexError('User not found.');
|
||||
return user;
|
||||
};
|
||||
|
||||
const latestStatusForOwner = async (
|
||||
ctx: RWCtx,
|
||||
ownerId: Id<'users'>,
|
||||
) => {
|
||||
const [latest] = await ctx.db
|
||||
.query('statuses')
|
||||
.withIndex('by_user_updatedAt', q => q.eq('userId', ownerId))
|
||||
.order('desc')
|
||||
.take(1);
|
||||
return latest as Doc<'statuses'> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new status for a single user.
|
||||
* - Defaults userId to the caller.
|
||||
* - updatedBy defaults to the caller.
|
||||
* - Updates the user's currentStatusId pointer.
|
||||
*/
|
||||
export const create = mutation({
|
||||
args: {
|
||||
message: v.string(),
|
||||
userId: v.optional(v.id('users')),
|
||||
updatedBy: v.optional(v.id('users')),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const authUserId = await getAuthUserId(ctx);
|
||||
if (!authUserId) throw new ConvexError('Not authenticated.');
|
||||
|
||||
const userId = args.userId ?? authUserId;
|
||||
await ensureUser(ctx, userId);
|
||||
|
||||
const updatedBy = args.updatedBy ?? authUserId;
|
||||
await ensureUser(ctx, updatedBy);
|
||||
|
||||
const message = args.message.trim();
|
||||
if (message.length === 0) {
|
||||
throw new ConvexError('Message cannot be empty.');
|
||||
}
|
||||
|
||||
const statusId = await ctx.db.insert('statuses', {
|
||||
message,
|
||||
userId,
|
||||
updatedBy,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
|
||||
await ctx.db.patch(userId, { currentStatusId: statusId });
|
||||
|
||||
return { statusId };
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Bulk create the same status for many users.
|
||||
* - updatedBy defaults to the caller.
|
||||
* - Updates each user's currentStatusId pointer.
|
||||
*/
|
||||
export const bulkCreate = mutation({
|
||||
args: {
|
||||
message: v.string(),
|
||||
ownerIds: v.array(v.id('users')),
|
||||
updatedBy: v.optional(v.id('users')),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const authUserId = await getAuthUserId(ctx);
|
||||
if (!authUserId) throw new ConvexError('Not authenticated.');
|
||||
|
||||
if (args.ownerIds.length === 0) return { statusIds: [] };
|
||||
|
||||
const updatedBy = args.updatedBy ?? authUserId;
|
||||
await ensureUser(ctx, updatedBy);
|
||||
|
||||
const message = args.message.trim();
|
||||
if (message.length === 0) {
|
||||
throw new ConvexError('Message cannot be empty.');
|
||||
}
|
||||
|
||||
const statusIds: Id<'statuses'>[] = [];
|
||||
const now = Date.now();
|
||||
|
||||
// Sequential to keep load predictable; switch to Promise.all
|
||||
// if your ownerIds lists are small and bounded.
|
||||
for (const userId of args.ownerIds) {
|
||||
await ensureUser(ctx, userId);
|
||||
|
||||
const statusId = await ctx.db.insert('statuses', {
|
||||
message,
|
||||
userId,
|
||||
updatedBy,
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
await ctx.db.patch(userId, { currentStatusId: statusId });
|
||||
statusIds.push(statusId);
|
||||
}
|
||||
|
||||
return { statusIds };
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Current status for a specific user.
|
||||
* - Uses users.currentStatusId if present,
|
||||
* otherwise falls back to latest by index.
|
||||
*/
|
||||
export const getCurrentForUser = query({
|
||||
args: { userId: v.id('users') },
|
||||
handler: async (ctx, { userId }) => {
|
||||
const user = await ensureUser(ctx, userId);
|
||||
|
||||
if (user.currentStatusId) {
|
||||
const status = await ctx.db.get(user.currentStatusId);
|
||||
if (status) return status;
|
||||
}
|
||||
|
||||
return await latestStatusForOwner(ctx, userId);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Current statuses for all users.
|
||||
* - Reads each user's currentStatusId pointer.
|
||||
* - Falls back to latest-by-index if pointer is missing.
|
||||
*/
|
||||
export const getCurrentForAll = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
const users = await ctx.db.query('users').collect();
|
||||
|
||||
const results = await Promise.all(
|
||||
users.map(async (u) => {
|
||||
let status = null;
|
||||
if (u.currentStatusId) {
|
||||
status = await ctx.db.get(u.currentStatusId);
|
||||
}
|
||||
status ??= await latestStatusForOwner(ctx, u._id);
|
||||
return {
|
||||
userId: u._id as Id<'users'>,
|
||||
status,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Paginated history for a specific user (newest first).
|
||||
*/
|
||||
export const listHistoryByUser = query({
|
||||
args: {
|
||||
userId: v.id('users'),
|
||||
paginationOpts: paginationOptsValidator,
|
||||
},
|
||||
handler: async (ctx, { userId, paginationOpts }) => {
|
||||
await ensureUser(ctx, userId);
|
||||
|
||||
return await ctx.db
|
||||
.query('statuses')
|
||||
.withIndex('by_user_updatedAt', q => q.eq('userId', userId))
|
||||
.order('desc')
|
||||
.paginate(paginationOpts);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Global paginated history (all users, newest first).
|
||||
* - Add an index on updatedAt if you want to avoid full-table scans
|
||||
* when the collection grows large.
|
||||
*/
|
||||
export const listHistoryAll = query({
|
||||
args: { paginationOpts: paginationOptsValidator },
|
||||
handler: async (ctx, { paginationOpts }) => {
|
||||
return await ctx.db
|
||||
.query('statuses')
|
||||
.order('desc')
|
||||
.paginate(paginationOpts);
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user