Add comments and tags to blog posts and display user info on comments

Implements blog post commenting and tagging functionality, and retrieves user information for displaying comments.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cbacfb18-842a-4116-a907-18c0105ad8ec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/39b5c689-6e8a-4d5a-9792-69cc81a56534/ecd8ef89-4efc-4907-bb4c-b74a23ed7e4a.jpg
This commit is contained in:
mukimovd 2025-05-01 03:43:32 +00:00
parent 78d07df844
commit bfbc4c336e
2 changed files with 94 additions and 3 deletions

View File

@ -2,6 +2,7 @@ import type { Express, Request, Response } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { z } from "zod";
import { NextFunction } from "express";
import {
insertUserSchema,
@ -16,7 +17,8 @@ import {
insertCollectionItemSchema,
insertTimelineEventSchema,
insertBlogPostSchema,
insertReadingProgressSchema
insertReadingProgressSchema,
Comment
} from "@shared/schema";
export async function registerRoutes(app: Express): Promise<Server> {
@ -471,9 +473,19 @@ export async function registerRoutes(app: Express): Promise<Server> {
const comments = await storage.getCommentsByEntityId('blogPost', post.id);
// Get enhanced comments with user info
const enhancedComments = await Promise.all(comments.map(async (comment) => {
const enhancedComments = await Promise.all(comments.map(async (comment: Comment) => {
// Create enhanced comment object
interface EnhancedComment extends Comment {
user?: {
id: number;
displayName: string | null;
avatar: string | null;
role: string;
};
}
const user = await storage.getUser(comment.userId);
return {
const enhancedComment: EnhancedComment = {
...comment,
user: user ? {
id: user.id,
@ -482,6 +494,8 @@ export async function registerRoutes(app: Express): Promise<Server> {
role: user.role
} : undefined
};
return enhancedComment;
}));
// Get like count

View File

@ -282,6 +282,46 @@ export class MemStorage implements IStorage {
title: "Death",
description: "Died in St. Petersburg from wounds received in a duel with Georges d'Anthès"
});
// Create sample blog post
const blogPost = this.createBlogPost({
title: "Exploring Russian Poetry in the Digital Age",
slug: "exploring-russian-poetry-digital-age",
content: `
# Exploring Russian Poetry in the Digital Age
The rich tradition of Russian poetry, from the romantic verses of Alexander Pushkin to the Soviet-era works of Anna Akhmatova, continues to captivate readers worldwide. In our increasingly digital world, how do we preserve and promote this literary heritage while making it accessible to new generations?
## The Digital Transformation of Literature
Digital platforms offer unprecedented opportunities for exploring Russian poetry:
- Side-by-side translations allow readers to compare different interpretations
- Interactive annotations provide historical and cultural context
- Audio recordings capture the rhythm and musicality of the original language
## Challenges and Opportunities
While digitization opens new doors, it also presents challenges. How do we balance modernization with respect for tradition? How can we ensure that the emotional impact of these works isn't lost in the transition from page to screen?
As we continue to develop the Tercul platform, we're committed to addressing these questions, creating an immersive experience that honors the depth and beauty of Russian poetic traditions while embracing the possibilities of digital innovation.
`,
authorId: user1.id,
excerpt: "Exploring how digital platforms can help preserve and promote the rich tradition of Russian poetry in the modern age.",
publishedAt: new Date()
});
// Add tags to the blog post
this.addTagToBlogPost(blogPost.id, poetryTag.id);
this.addTagToBlogPost(blogPost.id, romanticismTag.id);
// Add a comment to the blog post
this.createComment({
content: "Fascinating exploration of how technology can preserve literary traditions!",
userId: user1.id,
workId: 0, // Placeholder for blog posts
parentId: blogPost.id
});
}
// User methods
@ -436,6 +476,18 @@ export class MemStorage implements IStorage {
return Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
}
async getBlogPostTags(postId: number): Promise<Tag[]> {
const tagIds = this.blogPostTagsRelations.get(postId) || new Set();
return Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
}
async addTagToBlogPost(postId: number, tagId: number): Promise<void> {
if (!this.blogPostTagsRelations.has(postId)) {
this.blogPostTagsRelations.set(postId, new Set());
}
this.blogPostTagsRelations.get(postId)!.add(tagId);
}
// Bookmark methods
async getBookmarksByUserId(userId: number): Promise<Bookmark[]> {
return Array.from(this.bookmarks.values()).filter(
@ -484,6 +536,31 @@ export class MemStorage implements IStorage {
);
}
async getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]> {
// In our storage model, we have specific fields for workId and translationId,
// but we want to support a more generic entity approach for blog posts, etc.
// For now, we'll handle the mapping between entity types and specific fields
if (entityType === 'work') {
return this.getCommentsByWorkId(entityId);
} else if (entityType === 'translation') {
return this.getCommentsByTranslationId(entityId);
} else if (entityType === 'blogPost') {
// For blog posts, filter comments by their entityType and entityId properties
// This assumes we're storing comment entities with these properties
return Array.from(this.comments.values()).filter(
comment =>
// For blog posts, we're storing the references in the parentId field temporarily
// In a real implementation, we would add proper schema fields for this
comment.workId === 0 && // Use a placeholder workId
comment.parentId === entityId
);
}
// Default return empty array if entity type is not recognized
return [];
}
async createComment(insertComment: InsertComment): Promise<Comment> {
const id = this.commentId++;
const now = new Date();