- Created .gitignore to exclude sensitive files and directories. - Added API documentation in API_DOCUMENTATION.md. - Included deployment instructions in DEPLOYMENT.md. - Established project structure documentation in PROJECT_STRUCTURE.md. - Updated README.md with project status and team information. - Added recommendations and status tracking documents. - Introduced testing guidelines in TESTING.md. - Set up CI workflow in .github/workflows/ci.yml. - Created Dockerfile for backend and frontend setups. - Added various service and utility files for backend functionality. - Implemented frontend components and pages for user interface. - Included mobile app structure and services. - Established scripts for deployment across multiple chains.
166 lines
3.7 KiB
TypeScript
166 lines
3.7 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
export interface Comment {
|
|
id: string;
|
|
proposalId: bigint;
|
|
author: string;
|
|
content: string;
|
|
parentId?: string;
|
|
upvotes: number;
|
|
downvotes: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
|
|
export interface DiscussionThread {
|
|
proposalId: bigint;
|
|
comments: Comment[];
|
|
totalComments: number;
|
|
}
|
|
|
|
export class GovernanceDiscussionService {
|
|
/**
|
|
* Add comment to proposal
|
|
*/
|
|
async addComment(
|
|
proposalId: bigint,
|
|
author: string,
|
|
content: string,
|
|
parentId?: string
|
|
): Promise<Comment> {
|
|
const comment = await prisma.comment.create({
|
|
data: {
|
|
proposalId,
|
|
author,
|
|
content,
|
|
parentId,
|
|
upvotes: 0,
|
|
downvotes: 0,
|
|
},
|
|
});
|
|
|
|
return {
|
|
id: comment.id,
|
|
proposalId: comment.proposalId,
|
|
author: comment.author,
|
|
content: comment.content,
|
|
parentId: comment.parentId || undefined,
|
|
upvotes: comment.upvotes,
|
|
downvotes: comment.downvotes,
|
|
createdAt: comment.createdAt,
|
|
updatedAt: comment.updatedAt,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get discussion thread for proposal
|
|
*/
|
|
async getDiscussion(proposalId: bigint): Promise<DiscussionThread> {
|
|
const comments = await prisma.comment.findMany({
|
|
where: { proposalId },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
|
|
return {
|
|
proposalId,
|
|
comments: comments.map((c) => ({
|
|
id: c.id,
|
|
proposalId: c.proposalId,
|
|
author: c.author,
|
|
content: c.content,
|
|
parentId: c.parentId || undefined,
|
|
upvotes: c.upvotes,
|
|
downvotes: c.downvotes,
|
|
createdAt: c.createdAt,
|
|
updatedAt: c.updatedAt,
|
|
})),
|
|
totalComments: comments.length,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Vote on comment
|
|
*/
|
|
async voteComment(commentId: string, voter: string, upvote: boolean): Promise<void> {
|
|
const existingVote = await prisma.commentVote.findUnique({
|
|
where: {
|
|
commentId_voter: {
|
|
commentId,
|
|
voter,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (existingVote) {
|
|
// Update existing vote
|
|
if (existingVote.upvote !== upvote) {
|
|
await prisma.commentVote.update({
|
|
where: { id: existingVote.id },
|
|
data: { upvote },
|
|
});
|
|
|
|
// Update comment vote counts
|
|
if (upvote) {
|
|
await prisma.comment.update({
|
|
where: { id: commentId },
|
|
data: {
|
|
upvotes: { increment: 1 },
|
|
downvotes: { decrement: 1 },
|
|
},
|
|
});
|
|
} else {
|
|
await prisma.comment.update({
|
|
where: { id: commentId },
|
|
data: {
|
|
upvotes: { decrement: 1 },
|
|
downvotes: { increment: 1 },
|
|
},
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
// Create new vote
|
|
await prisma.commentVote.create({
|
|
data: {
|
|
commentId,
|
|
voter,
|
|
upvote,
|
|
},
|
|
});
|
|
|
|
// Update comment vote counts
|
|
await prisma.comment.update({
|
|
where: { id: commentId },
|
|
data: {
|
|
upvotes: upvote ? { increment: 1 } : undefined,
|
|
downvotes: upvote ? undefined : { increment: 1 },
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete comment
|
|
*/
|
|
async deleteComment(commentId: string, author: string): Promise<void> {
|
|
const comment = await prisma.comment.findUnique({
|
|
where: { id: commentId },
|
|
});
|
|
|
|
if (!comment) {
|
|
throw new Error('Comment not found');
|
|
}
|
|
|
|
if (comment.author.toLowerCase() !== author.toLowerCase()) {
|
|
throw new Error('Not authorized to delete this comment');
|
|
}
|
|
|
|
await prisma.comment.delete({
|
|
where: { id: commentId },
|
|
});
|
|
}
|
|
}
|
|
|