happy-server/sources/app/social/friendNotification.ts
2025-09-20 14:55:07 -07:00

170 lines
4.8 KiB
TypeScript

import { Prisma, RelationshipStatus } from "@prisma/client";
import { feedPost } from "@/app/feed/feedPost";
import { Context } from "@/context";
import { afterTx } from "@/storage/inTx";
/**
* Check if a notification should be sent based on the last notification time and relationship status.
* Returns true if:
* - No previous notification was sent (lastNotifiedAt is null)
* - OR 24 hours have passed since the last notification
* - AND the relationship is not rejected
*/
export function shouldSendNotification(
lastNotifiedAt: Date | null,
status: RelationshipStatus
): boolean {
// Don't send notifications for rejected relationships
if (status === RelationshipStatus.rejected) {
return false;
}
// If never notified, send notification
if (!lastNotifiedAt) {
return true;
}
// Check if 24 hours have passed since last notification
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
return lastNotifiedAt < twentyFourHoursAgo;
}
/**
* Send a friend request notification to the receiver and update lastNotifiedAt.
* This creates a feed item for the receiver about the incoming friend request.
*/
export async function sendFriendRequestNotification(
tx: Prisma.TransactionClient,
receiverUserId: string,
senderUserId: string
): Promise<void> {
// Check if we should send notification to receiver
const receiverRelationship = await tx.userRelationship.findUnique({
where: {
fromUserId_toUserId: {
fromUserId: receiverUserId,
toUserId: senderUserId
}
}
});
if (!receiverRelationship || !shouldSendNotification(
receiverRelationship.lastNotifiedAt,
receiverRelationship.status
)) {
return;
}
// Create feed notification for receiver
const receiverCtx = Context.create(receiverUserId);
await feedPost(
tx,
receiverCtx,
{
kind: 'friend_request',
uid: senderUserId
},
`friend_request_${senderUserId}` // repeatKey to avoid duplicates
);
// Update lastNotifiedAt for the receiver's relationship record
await tx.userRelationship.update({
where: {
fromUserId_toUserId: {
fromUserId: receiverUserId,
toUserId: senderUserId
}
},
data: {
lastNotifiedAt: new Date()
}
});
}
/**
* Send friendship established notifications to both users and update lastNotifiedAt.
* This creates feed items for both users about the new friendship.
*/
export async function sendFriendshipEstablishedNotification(
tx: Prisma.TransactionClient,
user1Id: string,
user2Id: string
): Promise<void> {
// Check and send notification to user1
const user1Relationship = await tx.userRelationship.findUnique({
where: {
fromUserId_toUserId: {
fromUserId: user1Id,
toUserId: user2Id
}
}
});
if (user1Relationship && shouldSendNotification(
user1Relationship.lastNotifiedAt,
user1Relationship.status
)) {
const user1Ctx = Context.create(user1Id);
await feedPost(
tx,
user1Ctx,
{
kind: 'friend_accepted',
uid: user2Id
},
`friend_accepted_${user2Id}` // repeatKey to avoid duplicates
);
// Update lastNotifiedAt for user1
await tx.userRelationship.update({
where: {
fromUserId_toUserId: {
fromUserId: user1Id,
toUserId: user2Id
}
},
data: {
lastNotifiedAt: new Date()
}
});
}
// Check and send notification to user2
const user2Relationship = await tx.userRelationship.findUnique({
where: {
fromUserId_toUserId: {
fromUserId: user2Id,
toUserId: user1Id
}
}
});
if (user2Relationship && shouldSendNotification(
user2Relationship.lastNotifiedAt,
user2Relationship.status
)) {
const user2Ctx = Context.create(user2Id);
await feedPost(
tx,
user2Ctx,
{
kind: 'friend_accepted',
uid: user1Id
},
`friend_accepted_${user1Id}` // repeatKey to avoid duplicates
);
// Update lastNotifiedAt for user2
await tx.userRelationship.update({
where: {
fromUserId_toUserId: {
fromUserId: user2Id,
toUserId: user1Id
}
},
data: {
lastNotifiedAt: new Date()
}
});
}
}