Hey is a decentralized and permissionless social media app built with Lens Protocol 🌿

refactor: update ProToggle component to enhance reusability and maintainability

yoginth.com 62626409 b23138c8

verified
+228
+20
apps/api/src/routes/chat/conversations/createConversation.ts
··· 1 + import { Status } from "@hey/data/enums"; 2 + import type { Context } from "hono"; 3 + import handleApiError from "@/utils/handleApiError"; 4 + import { chatService } from "./service"; 5 + 6 + const createConversation = async (ctx: Context) => { 7 + try { 8 + const { senderId, receiverId } = await ctx.req.json(); 9 + const conversation = await chatService.getOrCreateConversation( 10 + senderId, 11 + receiverId 12 + ); 13 + 14 + return ctx.json({ conversation, status: Status.Success }); 15 + } catch (error) { 16 + return handleApiError(ctx, error); 17 + } 18 + }; 19 + 20 + export default createConversation;
+22
apps/api/src/routes/chat/conversations/getConversation.ts
··· 1 + import { Status } from "@hey/data/enums"; 2 + import type { Context } from "hono"; 3 + import ApiError from "@/utils/apiError"; 4 + import handleApiError from "@/utils/handleApiError"; 5 + import { chatService } from "./service"; 6 + 7 + const getConversation = async (ctx: Context) => { 8 + try { 9 + const { conversationId } = ctx.req.param(); 10 + const conversation = await chatService.getConversation(conversationId); 11 + 12 + if (!conversation) { 13 + throw new ApiError(404, "Conversation not found."); 14 + } 15 + 16 + return ctx.json({ conversation, status: Status.Success }); 17 + } catch (error) { 18 + return handleApiError(ctx, error); 19 + } 20 + }; 21 + 22 + export default getConversation;
+35
apps/api/src/routes/chat/conversations/index.ts
··· 1 + import { zValidator } from "@hono/zod-validator"; 2 + import { Hono } from "hono"; 3 + import z from "zod"; 4 + import createConversation from "./createConversation"; 5 + import getConversation from "./getConversation"; 6 + import sendMessage from "./sendMessage"; 7 + 8 + const app = new Hono(); 9 + 10 + app.post( 11 + "/", 12 + zValidator( 13 + "json", 14 + z.object({ receiverId: z.string().min(1), senderId: z.string().min(1) }) 15 + ), 16 + createConversation 17 + ); 18 + 19 + app.get( 20 + "/:conversationId", 21 + zValidator("param", z.object({ conversationId: z.string().min(1) })), 22 + getConversation 23 + ); 24 + 25 + app.post( 26 + "/:conversationId/messages", 27 + zValidator("param", z.object({ conversationId: z.string().min(1) })), 28 + zValidator( 29 + "json", 30 + z.object({ content: z.string().min(1), senderId: z.string().min(1) }) 31 + ), 32 + sendMessage 33 + ); 34 + 35 + export default app;
+22
apps/api/src/routes/chat/conversations/sendMessage.ts
··· 1 + import { Status } from "@hey/data/enums"; 2 + import type { Context } from "hono"; 3 + import handleApiError from "@/utils/handleApiError"; 4 + import { chatService } from "./service"; 5 + 6 + const sendMessage = async (ctx: Context) => { 7 + try { 8 + const { conversationId } = ctx.req.param(); 9 + const { senderId, content } = await ctx.req.json(); 10 + const message = await chatService.sendMessage( 11 + conversationId, 12 + senderId, 13 + content 14 + ); 15 + 16 + return ctx.json({ message, status: Status.Success }); 17 + } catch (error) { 18 + return handleApiError(ctx, error); 19 + } 20 + }; 21 + 22 + export default sendMessage;
+121
apps/api/src/routes/chat/conversations/service.ts
··· 1 + import type { Prisma } from "@prisma/client"; 2 + import prisma from "@/utils/prisma"; 3 + 4 + const conversationInclude = { 5 + messages: { 6 + include: { 7 + sender: true 8 + }, 9 + orderBy: { 10 + createdAt: "asc" as const 11 + } 12 + }, 13 + receiver: true, 14 + sender: true 15 + } satisfies Prisma.ChatConversationInclude; 16 + 17 + const messageInclude = { 18 + conversation: { 19 + include: { 20 + receiver: true, 21 + sender: true 22 + } 23 + }, 24 + sender: true 25 + } satisfies Prisma.MessageInclude; 26 + 27 + export const chatService = { 28 + async ensureAccounts(accountIds: string[]) { 29 + const uniqueIds = Array.from(new Set(accountIds)); 30 + await Promise.all( 31 + uniqueIds.map((id) => 32 + prisma.lensAccount.upsert({ 33 + create: { id }, 34 + update: {}, 35 + where: { id } 36 + }) 37 + ) 38 + ); 39 + return uniqueIds; 40 + }, 41 + 42 + async getConversation(conversationId: string) { 43 + return prisma.chatConversation.findUnique({ 44 + include: conversationInclude, 45 + where: { id: conversationId } 46 + }); 47 + }, 48 + 49 + async getOrCreateConversation(senderId: string, receiverId: string) { 50 + if (senderId === receiverId) { 51 + throw new Error("Sender and receiver must be different accounts"); 52 + } 53 + 54 + await this.ensureAccounts([senderId, receiverId]); 55 + 56 + const sortedKey = [senderId, receiverId].sort().join(":"); 57 + 58 + const existing = await prisma.chatConversation.findUnique({ 59 + include: conversationInclude, 60 + where: { key: sortedKey } 61 + }); 62 + 63 + if (existing) { 64 + return existing; 65 + } 66 + 67 + return prisma.chatConversation.create({ 68 + data: { 69 + key: sortedKey, 70 + receiver: { 71 + connect: { id: receiverId } 72 + }, 73 + sender: { 74 + connect: { id: senderId } 75 + } 76 + }, 77 + include: conversationInclude 78 + }); 79 + }, 80 + 81 + async sendMessage(conversationId: string, senderId: string, content: string) { 82 + await this.ensureAccounts([senderId]); 83 + const conversation = await prisma.chatConversation.findUnique({ 84 + select: { 85 + id: true, 86 + receiverId: true, 87 + senderId: true 88 + }, 89 + where: { id: conversationId } 90 + }); 91 + 92 + if (!conversation) { 93 + throw new Error("Conversation not found"); 94 + } 95 + 96 + if ( 97 + conversation.senderId !== senderId && 98 + conversation.receiverId !== senderId 99 + ) { 100 + throw new Error("Sender is not part of the conversation"); 101 + } 102 + 103 + const message = await prisma.message.create({ 104 + data: { 105 + content, 106 + conversationId, 107 + senderId 108 + }, 109 + include: messageInclude 110 + }); 111 + 112 + await prisma.chatConversation.update({ 113 + data: { 114 + updatedAt: new Date() 115 + }, 116 + where: { id: conversationId } 117 + }); 118 + 119 + return message; 120 + } 121 + };
+8
apps/api/src/routes/chat/index.ts
··· 1 + import { Hono } from "hono"; 2 + import conversationsRouter from "./conversations"; 3 + 4 + const app = new Hono(); 5 + 6 + app.route("/conversations", conversationsRouter); 7 + 8 + export default app;