Added persistent statuses.

This commit is contained in:
2025-10-27 16:00:45 -05:00
parent 4306d69558
commit d2517901a8
12 changed files with 163 additions and 77 deletions

View File

@@ -8,11 +8,6 @@
* @module
*/
import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";
import type * as auth from "../auth.js";
import type * as crons from "../crons.js";
import type * as custom_auth_index from "../custom/auth/index.js";
@@ -23,6 +18,12 @@ import type * as files from "../files.js";
import type * as http from "../http.js";
import type * as statuses from "../statuses.js";
import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*
@@ -42,11 +43,15 @@ declare const fullApi: ApiFromModules<{
http: typeof http;
statuses: typeof statuses;
}>;
declare const fullApiWithMounts: typeof fullApi;
export declare const api: FilterApi<
typeof fullApi,
typeof fullApiWithMounts,
FunctionReference<any, "public">
>;
export declare const internal: FilterApi<
typeof fullApi,
typeof fullApiWithMounts,
FunctionReference<any, "internal">
>;
export declare const components: {};

View File

@@ -8,7 +8,7 @@
* @module
*/
import { anyApi } from "convex/server";
import { anyApi, componentsGeneric } from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
@@ -20,3 +20,4 @@ import { anyApi } from "convex/server";
*/
export const api = anyApi;
export const internal = anyApi;
export const components = componentsGeneric();

View File

@@ -10,6 +10,7 @@
import {
ActionBuilder,
AnyComponents,
HttpActionBuilder,
MutationBuilder,
QueryBuilder,
@@ -18,9 +19,15 @@ import {
GenericQueryCtx,
GenericDatabaseReader,
GenericDatabaseWriter,
FunctionReference,
} from "convex/server";
import type { DataModel } from "./dataModel.js";
type GenericCtx =
| GenericActionCtx<DataModel>
| GenericMutationCtx<DataModel>
| GenericQueryCtx<DataModel>;
/**
* Define a query in this Convex app's public API.
*

View File

@@ -16,6 +16,7 @@ import {
internalActionGeneric,
internalMutationGeneric,
internalQueryGeneric,
componentsGeneric,
} from "convex/server";
/**

View File

@@ -1,7 +1,7 @@
// convex/crons.ts
import { cronJobs } from 'convex/server';
import { api } from './_generated/api';
// Cron order: Minute Hour DayOfMonth Month DayOfWeek
const crons = cronJobs();
crons.cron(
@@ -13,9 +13,9 @@ crons.cron(
);
crons.cron(
'End of shift (weekdays 5pm CT)',
// Run at 4:00 PM CST / 5:00 PM CDT
// Only on weekdays
'End of shift (weekdays 5pm CT)',
'0 22 * * 1-5',
api.statuses.endOfShiftUpdate,
);

View File

@@ -26,6 +26,7 @@ export default defineSchema({
message: v.string(),
updatedAt: v.number(),
updatedBy: v.optional(v.id('users')),
persistentStatus: v.optional(v.boolean()),
})
.index('by_user', ['userId'])
.index('by_user_updatedAt', ['userId', 'updatedAt']),

View File

@@ -24,6 +24,7 @@ type StatusRow = {
message: string;
updatedAt: number;
updatedBy: StatusRow['user'] | null;
persistentStatus: boolean;
} | null;
};
@@ -56,6 +57,7 @@ export const create = mutation({
message: v.string(),
userId: v.optional(v.id('users')),
updatedBy: v.optional(v.id('users')),
persistentStatus: v.optional(v.boolean()),
},
handler: async (ctx, args) => {
const authUserId: Id<'users'> | null = await getAuthUserId(ctx);
@@ -68,6 +70,7 @@ export const create = mutation({
}
const userId = args.userId ?? authUserId!;
const updatedBy = args.updatedBy ?? authUserId;
const persistentStatus = args.persistentStatus ?? false;
await ensureUser(ctx, userId);
let statusId: Id<'statuses'>;
if (updatedBy) {
@@ -77,12 +80,14 @@ export const create = mutation({
userId,
updatedBy,
updatedAt: Date.now(),
persistentStatus,
});
} else {
statusId = await ctx.db.insert('statuses', {
message,
userId,
updatedAt: Date.now(),
persistentStatus,
});
}
await ctx.db.patch(userId, { currentStatusId: statusId });
@@ -95,6 +100,7 @@ export const bulkCreate = mutation({
message: v.string(),
userIds: v.array(v.id('users')),
updatedBy: v.optional(v.id('users')),
persistentStatus: v.optional(v.boolean()),
},
handler: async (ctx, args) => {
const authUserId = await getAuthUserId(ctx);
@@ -103,6 +109,7 @@ export const bulkCreate = mutation({
if (args.userIds.length === 0) return { statusIds: [] };
const updatedBy = args.updatedBy ?? authUserId;
const persistentStatus = args.persistentStatus ?? false;
await ensureUser(ctx, updatedBy);
const message = args.message.trim();
@@ -120,6 +127,7 @@ export const bulkCreate = mutation({
userId,
updatedBy,
updatedAt: now,
persistentStatus,
});
await ctx.db.patch(userId, { currentStatusId: statusId });
statusIds.push(statusId);
@@ -130,10 +138,14 @@ export const bulkCreate = mutation({
});
export const updateAllStatuses = mutation({
args: { message: v.string() },
args: {
message: v.string(),
persistentStatus: v.optional(v.boolean())
},
handler: async (ctx, args) => {
const users = await ctx.db.query('users').collect();
const message = args.message.trim();
const persistentStatus = args.persistentStatus ?? false;
if (message.length === 0) {
throw new ConvexError('Message cannot be empty.');
}
@@ -142,15 +154,20 @@ export const updateAllStatuses = mutation({
const now = Date.now();
for (const user of users) {
const statusId = await ctx.db.insert('statuses', {
message,
const curStatus = await ctx.runQuery(api.statuses.getCurrentForUser, {
userId: user._id,
updatedAt: now,
});
await ctx.db.patch(user._id, { currentStatusId: statusId });
statusIds.push(statusId);
if (!curStatus?.persistentStatus) {
const statusId = await ctx.db.insert('statuses', {
message,
userId: user._id,
updatedAt: now,
persistentStatus,
});
await ctx.db.patch(user._id, { currentStatusId: statusId });
statusIds.push(statusId);
}
}
return { statusIds };
},
});
@@ -161,6 +178,12 @@ export const createLunchStatus = mutation({
const authUserId = await getAuthUserId(ctx);
const lunchUserId = args.userId ?? authUserId
if (!lunchUserId) throw new ConvexError('Not authenticated.');
const curStatus = await ctx.runQuery(api.statuses.getCurrentForUser, {
userId: lunchUserId,
});
if (curStatus?.persistentStatus) {
return { success: false, error: 'Current status is persistent.'};
}
await ctx.runMutation(api.statuses.create, {
message: 'At lunch',
userId: lunchUserId,
@@ -179,6 +202,12 @@ export const backFromLunchStatus = mutation({
const authUserId = await getAuthUserId(ctx);
const lunchUserId = args.userId ?? authUserId
if (!lunchUserId) throw new ConvexError('Not authenticated.');
const curStatus = await ctx.runQuery(api.statuses.getCurrentForUser, {
userId: lunchUserId,
});
if (curStatus?.persistentStatus) {
return { success: false, error: 'Current status is persistent.'};
}
const user = await ensureUser(ctx, lunchUserId);
if (!user.currentStatusId) throw new ConvexError('User has no current status.');
const currentStatus = await ctx.db.get(user.currentStatusId);
@@ -253,6 +282,7 @@ export const getCurrentForAll = query({
message: curStatus.message,
updatedAt: curStatus.updatedAt,
updatedBy: updatedByUser,
persistentStatus: curStatus.persistentStatus ?? false,
}
: null;
return {
@@ -318,6 +348,7 @@ export const listHistory = query({
message: s.message,
updatedAt: s.updatedAt,
updatedBy,
persistentStatus: s.persistentStatus ?? false,
},
});
}