mirror of
https://github.com/SamyRai/tercul-frontend.git
synced 2025-12-27 04:51:34 +00:00
Refactors `MemStorage` to read initial data from `data/sampleData.json` and creates `Translation` entries directly. 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/eb989528-3752-4578-8a9b-855b9310e0d3.jpg
1190 lines
46 KiB
TypeScript
1190 lines
46 KiB
TypeScript
import {
|
||
users, User, InsertUser,
|
||
authors, Author, InsertAuthor,
|
||
works, Work, InsertWork,
|
||
translations, Translation, InsertTranslation,
|
||
tags, Tag, InsertTag,
|
||
workTags,
|
||
bookmarks, Bookmark, InsertBookmark,
|
||
comments, Comment, InsertComment,
|
||
likes, Like, InsertLike,
|
||
collections, Collection, InsertCollection,
|
||
collectionItems, CollectionItem, InsertCollectionItem,
|
||
timelineEvents, TimelineEvent, InsertTimelineEvent,
|
||
blogPosts, BlogPost, InsertBlogPost,
|
||
readingProgress, ReadingProgress, InsertReadingProgress
|
||
} from "@shared/schema";
|
||
|
||
export interface IStorage {
|
||
// User methods
|
||
getUser(id: number): Promise<User | undefined>;
|
||
getUserByUsername(username: string): Promise<User | undefined>;
|
||
createUser(user: InsertUser): Promise<User>;
|
||
|
||
// Author methods
|
||
getAuthors(limit?: number, offset?: number): Promise<Author[]>;
|
||
getAuthorById(id: number): Promise<Author | undefined>;
|
||
getAuthorBySlug(slug: string): Promise<Author | undefined>;
|
||
createAuthor(author: InsertAuthor): Promise<Author>;
|
||
|
||
// Work methods
|
||
getWorks(limit?: number, offset?: number): Promise<Work[]>;
|
||
getWorkById(id: number): Promise<Work | undefined>;
|
||
getWorkBySlug(slug: string): Promise<Work | undefined>;
|
||
getWorksByAuthorId(authorId: number): Promise<Work[]>;
|
||
getWorksByTags(tagIds: number[]): Promise<Work[]>;
|
||
createWork(work: InsertWork): Promise<Work>;
|
||
|
||
// Translation methods
|
||
getTranslations(workId: number): Promise<Translation[]>;
|
||
getTranslationById(id: number): Promise<Translation | undefined>;
|
||
createTranslation(translation: InsertTranslation): Promise<Translation>;
|
||
|
||
// Tag methods
|
||
getTags(): Promise<Tag[]>;
|
||
getTagsByType(type: string): Promise<Tag[]>;
|
||
createTag(tag: InsertTag): Promise<Tag>;
|
||
addTagToWork(workId: number, tagId: number): Promise<void>;
|
||
getWorkTags(workId: number): Promise<Tag[]>;
|
||
getBlogPostTags(postId: number): Promise<Tag[]>;
|
||
|
||
// Bookmark methods
|
||
getBookmarksByUserId(userId: number): Promise<Bookmark[]>;
|
||
getBookmarkByUserAndWork(userId: number, workId: number): Promise<Bookmark | undefined>;
|
||
createBookmark(bookmark: InsertBookmark): Promise<Bookmark>;
|
||
deleteBookmark(id: number): Promise<void>;
|
||
|
||
// Comment methods
|
||
getCommentsByWorkId(workId: number): Promise<Comment[]>;
|
||
getCommentsByTranslationId(translationId: number): Promise<Comment[]>;
|
||
getCommentsByUserId(userId: number): Promise<Comment[]>;
|
||
getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]>;
|
||
createComment(comment: InsertComment): Promise<Comment>;
|
||
|
||
// Like methods
|
||
createLike(like: InsertLike): Promise<Like>;
|
||
deleteLike(id: number): Promise<void>;
|
||
getLikesByEntity(entityType: string, entityId: number): Promise<Like[]>;
|
||
getLikesByUserId(userId: number): Promise<Like[]>;
|
||
|
||
// Collection methods
|
||
getCollections(limit?: number, offset?: number): Promise<Collection[]>;
|
||
getCollectionsByUserId(userId: number): Promise<Collection[]>;
|
||
getCollectionById(id: number): Promise<Collection | undefined>;
|
||
getCollectionBySlug(slug: string): Promise<Collection | undefined>;
|
||
createCollection(collection: InsertCollection): Promise<Collection>;
|
||
addWorkToCollection(collectionItem: InsertCollectionItem): Promise<CollectionItem>;
|
||
getCollectionItems(collectionId: number): Promise<CollectionItem[]>;
|
||
|
||
// Timeline events
|
||
getTimelineEventsByAuthorId(authorId: number): Promise<TimelineEvent[]>;
|
||
createTimelineEvent(event: InsertTimelineEvent): Promise<TimelineEvent>;
|
||
|
||
// Blog posts
|
||
getBlogPosts(limit?: number, offset?: number): Promise<BlogPost[]>;
|
||
getBlogPostById(id: number): Promise<BlogPost | undefined>;
|
||
getBlogPostBySlug(slug: string): Promise<BlogPost | undefined>;
|
||
createBlogPost(post: InsertBlogPost): Promise<BlogPost>;
|
||
|
||
// Reading progress
|
||
getReadingProgress(userId: number, workId: number, translationId?: number): Promise<ReadingProgress | undefined>;
|
||
updateReadingProgress(progress: InsertReadingProgress): Promise<ReadingProgress>;
|
||
|
||
// Search and filter methods
|
||
searchWorks(query: string, limit?: number): Promise<Work[]>;
|
||
searchAuthors(query: string, limit?: number): Promise<Author[]>;
|
||
filterWorks(params: { language?: string, type?: string, yearStart?: number, yearEnd?: number, tags?: number[] }): Promise<Work[]>;
|
||
}
|
||
|
||
export class MemStorage implements IStorage {
|
||
private users: Map<number, User>;
|
||
private authors: Map<number, Author>;
|
||
private works: Map<number, Work>;
|
||
private translations: Map<number, Translation>;
|
||
private tags: Map<number, Tag>;
|
||
private workTagsRelations: Map<number, Set<number>>;
|
||
private blogPostTagsRelations: Map<number, Set<number>>;
|
||
private bookmarks: Map<number, Bookmark>;
|
||
private comments: Map<number, Comment>;
|
||
private likes: Map<number, Like>;
|
||
private collections: Map<number, Collection>;
|
||
private collectionItems: Map<number, CollectionItem>;
|
||
private timelineEvents: Map<number, TimelineEvent>;
|
||
private blogPosts: Map<number, BlogPost>;
|
||
private readingProgresses: Map<string, ReadingProgress>;
|
||
|
||
private userId: number = 1;
|
||
private authorId: number = 1;
|
||
private workId: number = 1;
|
||
private translationId: number = 1;
|
||
private tagId: number = 1;
|
||
private bookmarkId: number = 1;
|
||
private commentId: number = 1;
|
||
private likeId: number = 1;
|
||
private collectionId: number = 1;
|
||
private collectionItemId: number = 1;
|
||
private timelineEventId: number = 1;
|
||
private blogPostId: number = 1;
|
||
private readingProgressId: number = 1;
|
||
|
||
constructor() {
|
||
this.users = new Map();
|
||
this.authors = new Map();
|
||
this.works = new Map();
|
||
this.translations = new Map();
|
||
this.tags = new Map();
|
||
this.workTagsRelations = new Map();
|
||
this.blogPostTagsRelations = new Map();
|
||
this.bookmarks = new Map();
|
||
this.comments = new Map();
|
||
this.likes = new Map();
|
||
this.collections = new Map();
|
||
this.collectionItems = new Map();
|
||
this.timelineEvents = new Map();
|
||
this.blogPosts = new Map();
|
||
this.readingProgresses = new Map();
|
||
|
||
// Initialize with some data
|
||
this.initializeData();
|
||
}
|
||
|
||
private initializeData() {
|
||
// Create sample users with different roles
|
||
const admin = this.createUser({
|
||
username: "admin",
|
||
password: "password",
|
||
email: "admin@example.com",
|
||
displayName: "Administrator",
|
||
role: "admin",
|
||
bio: "Platform administrator and content curator",
|
||
avatar: "https://i.pravatar.cc/150?u=admin"
|
||
});
|
||
|
||
const editor = this.createUser({
|
||
username: "editor",
|
||
password: "password",
|
||
email: "editor@example.com",
|
||
displayName: "Content Editor",
|
||
role: "editor",
|
||
bio: "Professional editor with expertise in Russian literature",
|
||
avatar: "https://i.pravatar.cc/150?u=editor"
|
||
});
|
||
|
||
const translator = this.createUser({
|
||
username: "translator",
|
||
password: "password",
|
||
email: "translator@example.com",
|
||
displayName: "Literary Translator",
|
||
role: "translator",
|
||
bio: "Certified translator specializing in Slavic languages",
|
||
avatar: "https://i.pravatar.cc/150?u=translator"
|
||
});
|
||
|
||
const reader = this.createUser({
|
||
username: "booklover",
|
||
password: "password",
|
||
email: "reader@example.com",
|
||
displayName: "Book Enthusiast",
|
||
role: "user",
|
||
bio: "Avid reader and literature fan",
|
||
avatar: "https://i.pravatar.cc/150?u=reader"
|
||
});
|
||
|
||
const scholar = this.createUser({
|
||
username: "scholar",
|
||
password: "password",
|
||
email: "scholar@example.com",
|
||
displayName: "Literary Scholar",
|
||
role: "user",
|
||
bio: "PhD in Comparative Literature focusing on Romanticism",
|
||
avatar: "https://i.pravatar.cc/150?u=scholar"
|
||
});
|
||
|
||
// Create sample authors
|
||
const pushkin = this.createAuthor({
|
||
name: "Alexander Pushkin",
|
||
slug: "alexander-pushkin",
|
||
birthYear: 1799,
|
||
deathYear: 1837,
|
||
country: "Russia",
|
||
biography: "Alexander Sergeyevich Pushkin was a Russian poet, playwright, and novelist of the Romantic era. He is considered by many to be the greatest Russian poet and the founder of modern Russian literature. Pushkin was born into Russian nobility in Moscow. His father, Sergey Lvovich Pushkin, was descended from a distinguished family of the Russian nobility that traced its ancestry back to the 12th century. Pushkin's works embraced a wide variety of literary forms, from drama and lyric poetry to literary criticism, fairy tales, and historical narrative.",
|
||
portrait: "https://images.unsplash.com/photo-1541688329375-fd29c1b13276?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
|
||
createdBy: admin.id
|
||
});
|
||
|
||
const tolstoy = this.createAuthor({
|
||
name: "Leo Tolstoy",
|
||
slug: "leo-tolstoy",
|
||
birthYear: 1828,
|
||
deathYear: 1910,
|
||
country: "Russia",
|
||
biography: "Count Lev Nikolayevich Tolstoy, usually referred to in English as Leo Tolstoy, was a Russian writer who is regarded as one of the greatest authors of all time. He received nominations for the Nobel Prize in Literature every year from 1902 to 1906 and for the Nobel Peace Prize in 1901, 1902, and 1909. Born to an aristocratic Russian family in 1828, Tolstoy's notable works include the novels War and Peace (1869) and Anna Karenina (1878), often cited as pinnacles of realist fiction.",
|
||
portrait: "https://images.unsplash.com/photo-1583583214294-20e9a2c176a6?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
|
||
createdBy: editor.id
|
||
});
|
||
|
||
const dostoevsky = this.createAuthor({
|
||
name: "Fyodor Dostoevsky",
|
||
slug: "fyodor-dostoevsky",
|
||
birthYear: 1821,
|
||
deathYear: 1881,
|
||
country: "Russia",
|
||
biography: "Fyodor Mikhailovich Dostoevsky was a Russian novelist, philosopher, short story writer, essayist, and journalist. His literary works explore human psychology in the troubled political, social, and spiritual atmospheres of 19th-century Russia, and engage with a variety of philosophical and religious themes. His most acclaimed novels include Crime and Punishment (1866), The Idiot (1869), Demons (1872), and The Brothers Karamazov (1880). Dostoevsky's body of work consists of 12 novels, four novellas, 16 short stories, and numerous other works.",
|
||
portrait: "https://images.unsplash.com/photo-1589229722039-eb062f5d834c?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80",
|
||
createdBy: editor.id
|
||
});
|
||
|
||
// Create sample tags (more extensive set)
|
||
const lyricTag = this.createTag({ name: "Lyric", type: "genre" });
|
||
const loveTag = this.createTag({ name: "Love", type: "theme" });
|
||
const poetryTag = this.createTag({ name: "Poetry", type: "genre" });
|
||
const romanticismTag = this.createTag({ name: "Romanticism", type: "period" });
|
||
const novelTag = this.createTag({ name: "Novel", type: "genre" });
|
||
const verseTag = this.createTag({ name: "Verse", type: "form" });
|
||
const historyTag = this.createTag({ name: "Historical", type: "theme" });
|
||
const gothicTag = this.createTag({ name: "Gothic", type: "genre" });
|
||
const fictionTag = this.createTag({ name: "Fiction", type: "genre" });
|
||
const warTag = this.createTag({ name: "War", type: "theme" });
|
||
const philosophyTag = this.createTag({ name: "Philosophy", type: "theme" });
|
||
const russianTag = this.createTag({ name: "Russian", type: "language" });
|
||
const epicTag = this.createTag({ name: "Epic", type: "genre" });
|
||
const classicTag = this.createTag({ name: "Classic", type: "period" });
|
||
const socialCriticismTag = this.createTag({ name: "Social Criticism", type: "theme" });
|
||
const existentialismTag = this.createTag({ name: "Existentialism", type: "theme" });
|
||
const psychologicalTag = this.createTag({ name: "Psychological", type: "genre" });
|
||
const deathTag = this.createTag({ name: "Death", type: "theme" });
|
||
const realismTag = this.createTag({ name: "Realism", type: "period" });
|
||
|
||
// Create sample works with detailed content
|
||
const lovedYouWork = this.createWork({
|
||
title: "I Loved You",
|
||
slug: "i-loved-you",
|
||
authorId: pushkin.id,
|
||
year: 1829,
|
||
type: "poem",
|
||
language: "Russian",
|
||
content: "Я вас любил: любовь еще, быть может,\nВ душе моей угасла не совсем;\nНо пусть она вас больше не тревожит;\nЯ не хочу печалить вас ничем.\nЯ вас любил безмолвно, безнадежно,\nТо робостью, то ревностью томим;\nЯ вас любил так искренно, так нежно,\nКак дай вам Бог любимой быть другим.",
|
||
description: "A short lyrical poem expressing unselfish love and one of Pushkin's most famous works. The poem describes the narrator's admiration for a beloved from whom he has parted. Written in 1829 and first published in 1830, this work showcases Pushkin's ability to express profound emotion with elegant simplicity. The poem's conclusion is particularly notable for its generous sentiment, wishing that the beloved will be loved by someone else as genuinely as the narrator loved her.",
|
||
createdBy: admin.id
|
||
});
|
||
|
||
// Add translations for "I Loved You"
|
||
const englishTranslation1 = this.createTranslation({
|
||
workId: lovedYouWork.id,
|
||
translatorId: translator.id,
|
||
language: "English",
|
||
year: 1930,
|
||
title: "I Loved You (Avrahm Yarmolinsky Translation)",
|
||
content: "I loved you; even now, I may confess,\nWithin my heart, the embers yet are warm;\nBut do not let it cause you more distress,\nI do not want to sadden you or harm.\nI loved you without hope, without a word,\nIn jealousy and fear, I've been the slave,\nI loved you with such passion, such pure love,\nAs may your future lover never have.",
|
||
description: "A classic translation by Avrahm Yarmolinsky that tries to preserve the rhyme scheme and meter of the original Russian poem."
|
||
});
|
||
|
||
const englishTranslation2 = this.createTranslation({
|
||
workId: lovedYouWork.id,
|
||
translatorId: scholar.id,
|
||
language: "English",
|
||
year: 2001,
|
||
title: "I Loved You (Modern Translation)",
|
||
content: "I loved you once, and still, perhaps, love's yearning\nWithin my soul has not quite died away.\nBut let it not dismay you any longer;\nI do not wish to cause you any pain.\nI loved you wordlessly, without hope, shyly,\nNow jealous and now diffident and shy;\nI loved you with such tenderness, such candor,\nAs God grant you be loved by and by.",
|
||
description: "A more modern translation that focuses on conveying the emotional content rather than strict adherence to form."
|
||
});
|
||
|
||
const frenchTranslation = this.createTranslation({
|
||
workId: lovedYouWork.id,
|
||
translatorId: translator.id,
|
||
language: "French",
|
||
year: 1947,
|
||
title: "Je vous aimais",
|
||
content: "Je vous aimais: peut-être cet amour\nN'est-il pas tout à fait éteint dans mon âme;\nMais ne vous tourmentez plus désormais;\nJe ne veux plus rien qui vous afflige.\nJe vous aimais sans rien dire, sans espoir,\nTour à tour timide et jaloux;\nJe vous aimais avec tant de tendresse, tant de sincérité,\nQue Dieu veuille que vous soyez aimée ainsi par un autre.",
|
||
description: "A French translation that captures the lyrical quality of Pushkin's original poem."
|
||
});
|
||
|
||
const germanTranslation = this.createTranslation({
|
||
workId: lovedYouWork.id,
|
||
translatorId: scholar.id,
|
||
language: "German",
|
||
year: 1952,
|
||
title: "Ich liebte Sie",
|
||
content: "Ich liebte Sie, und diese Liebe mag\nIn meiner Seele noch nicht ganz verklungen sein,\nDoch soll sie Ihnen keinen Kummer bringen,\nIch will Sie nicht betrüben, das soll nicht sein.\nIch liebte Sie so hoffnungslos, so stumm,\nVon Schüchternheit, von Eifersucht gequält,\nIch liebte Sie so treu, so zärtlich, wie\nGott gebe, daß Sie jemand so noch liebt.",
|
||
description: "A German translation that maintains the formal structure while conveying the emotional depth of the original."
|
||
});
|
||
|
||
// Add tags to "I Loved You" work
|
||
this.addTagToWork(lovedYouWork.id, lyricTag.id);
|
||
this.addTagToWork(lovedYouWork.id, loveTag.id);
|
||
this.addTagToWork(lovedYouWork.id, poetryTag.id);
|
||
this.addTagToWork(lovedYouWork.id, romanticismTag.id);
|
||
this.addTagToWork(lovedYouWork.id, russianTag.id);
|
||
|
||
// Add comments to the work and translations
|
||
this.createComment({
|
||
userId: reader.id,
|
||
entityType: "work",
|
||
entityId: lovedYouWork.id,
|
||
content: "This is one of my favorite Pushkin poems. The selflessness expressed in the final lines is truly moving.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: scholar.id,
|
||
entityType: "work",
|
||
entityId: lovedYouWork.id,
|
||
content: "The poem's structure reinforces its emotional journey. Notice how the final couplet resolves the tension built throughout the octave.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: translator.id,
|
||
entityType: "translation",
|
||
entityId: englishTranslation1.id,
|
||
content: "This translation captures the rhyme scheme well, but I find it loses some of the gentle tone of the original Russian.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: editor.id,
|
||
entityType: "translation",
|
||
entityId: englishTranslation2.id,
|
||
content: "The modern translation does a better job of conveying the emotional nuance, particularly in the last two lines.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
// Add line-specific annotations
|
||
this.createComment({
|
||
userId: scholar.id,
|
||
entityType: "line",
|
||
entityId: lovedYouWork.id,
|
||
lineNumber: 1,
|
||
content: "The opening phrase 'Я вас любил' (I loved you) is stated in the past tense, immediately establishing the poem's premise of a love that has ended.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: scholar.id,
|
||
entityType: "line",
|
||
entityId: lovedYouWork.id,
|
||
lineNumber: 4,
|
||
content: "This line demonstrates the narrator's selfless concern for the beloved's happiness.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: translator.id,
|
||
entityType: "line",
|
||
entityId: lovedYouWork.id,
|
||
lineNumber: 7,
|
||
content: "The repetition of 'так' (so) emphasizes the depth and purity of the speaker's love.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
this.createComment({
|
||
userId: reader.id,
|
||
entityType: "line",
|
||
entityId: lovedYouWork.id,
|
||
lineNumber: 8,
|
||
content: "The final line is what makes this poem so powerful - wishing for someone else to love her as genuinely as he did.",
|
||
createdAt: new Date()
|
||
});
|
||
|
||
// Add likes
|
||
this.createLike({
|
||
userId: reader.id,
|
||
entityType: "work",
|
||
entityId: lovedYouWork.id
|
||
});
|
||
|
||
this.createLike({
|
||
userId: scholar.id,
|
||
entityType: "work",
|
||
entityId: lovedYouWork.id
|
||
});
|
||
|
||
this.createLike({
|
||
userId: translator.id,
|
||
entityType: "work",
|
||
entityId: lovedYouWork.id
|
||
});
|
||
|
||
this.createLike({
|
||
userId: editor.id,
|
||
entityType: "translation",
|
||
entityId: englishTranslation2.id
|
||
});
|
||
|
||
this.createLike({
|
||
userId: reader.id,
|
||
entityType: "translation",
|
||
entityId: frenchTranslation.id
|
||
});
|
||
|
||
// Add bookmarks
|
||
this.createBookmark({
|
||
userId: reader.id,
|
||
workId: lovedYouWork.id,
|
||
note: "Beautiful love poem to revisit"
|
||
});
|
||
|
||
this.createBookmark({
|
||
userId: scholar.id,
|
||
workId: lovedYouWork.id,
|
||
note: "Good example of Pushkin's lyrical style"
|
||
});
|
||
|
||
// Create timeline events for Pushkin
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1799,
|
||
title: "Birth",
|
||
description: "Alexander Pushkin was born in Moscow to a noble family."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1811,
|
||
title: "Lyceum",
|
||
description: "Entered the prestigious Imperial Lyceum at Tsarskoye Selo."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1820,
|
||
title: "Southern Exile",
|
||
description: "Exiled to the south of Russia due to his political verses."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1824,
|
||
title: "Northern Exile",
|
||
description: "Confined to his family estate at Mikhailovskoye."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1826,
|
||
title: "Return from Exile",
|
||
description: "Pardoned by Tsar Nicholas I and allowed to return from exile."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1829,
|
||
title: "Composition of 'I Loved You'",
|
||
description: "Wrote the poem 'I Loved You', possibly inspired by his complicated relationship with Anna Olenina."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1830,
|
||
title: "Engagement",
|
||
description: "Became engaged to Natalia Goncharova."
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1837,
|
||
title: "Death",
|
||
description: "Fatally wounded in a duel with Georges d'Anthès over the honor of his wife, and died two days later."
|
||
});
|
||
|
||
// Create a collection
|
||
const pushkinCollection = this.createCollection({
|
||
title: "Essential Pushkin",
|
||
slug: "essential-pushkin",
|
||
description: "A collection of Alexander Pushkin's most important works, showcasing his range and significance in Russian literature.",
|
||
userId: scholar.id,
|
||
isPublic: true
|
||
});
|
||
|
||
this.addWorkToCollection({
|
||
collectionId: pushkinCollection.id,
|
||
workId: lovedYouWork.id,
|
||
order: 1,
|
||
note: "A perfect introduction to Pushkin's lyrical poetry."
|
||
});
|
||
|
||
// Create a blog post about literary analysis
|
||
const analysisPost = this.createBlogPost({
|
||
title: "The Art of Selflessness in Pushkin's 'I Loved You'",
|
||
slug: "selflessness-pushkins-i-loved-you",
|
||
content: "# The Art of Selflessness in Pushkin's 'I Loved You'\n\nAlexander Pushkin's short lyric 'I Loved You' (Я вас любил) stands as one of the most profound expressions of selfless love in world literature. Written in 1829 and published in 1830, this eight-line poem has captivated readers for nearly two centuries.\n\n## The Power of Simplicity\n\nWhat makes this poem so enduring is its elegant simplicity. In just eight lines, Pushkin creates a complete emotional journey from past love to present generosity of spirit. The poem begins with a straightforward declaration—'I loved you'—establishing both the subject and the past tense nature of the emotion.\n\n## Linguistic Analysis\n\nThe original Russian employs a formal 'you' (вас) throughout, creating a sense of respectful distance between the speaker and the addressee. This formality contrasts with the deeply personal nature of the sentiments expressed, creating a tension that underscores the speaker's restraint.\n\n## The Culminating Wish\n\nThe poem's final line—'Как дай вам Бог любимой быть другим' (As God grant you be loved by another)—represents the apex of selfless love in literature. Here, the speaker doesn't merely accept that his beloved will love another; he actively wishes that she will experience a love as genuine and deep as his own.\n\n## Translation Challenges\n\nTranslators have long struggled with capturing the perfect balance of formal structure and emotional resonance in this poem. The challenge lies in preserving the musicality of Pushkin's iambic tetrameter while conveying the subtle emotional shadings of the original Russian.\n\n## Conclusion\n\nPushkin's 'I Loved You' demonstrates how the most profound human emotions can be expressed with remarkable economy and precision. Its enduring appeal lies in its universal theme—the selfless wish for another's happiness—expressed with a sincerity that transcends cultural and temporal boundaries.",
|
||
authorId: scholar.id,
|
||
publishedAt: new Date(),
|
||
featured: true
|
||
});
|
||
|
||
const eugeneOnegin = this.createWork({
|
||
title: "Eugene Onegin",
|
||
slug: "eugene-onegin",
|
||
authorId: pushkin.id,
|
||
year: 1833,
|
||
type: "poem",
|
||
language: "Russian",
|
||
content: "Sample content for Eugene Onegin...",
|
||
description: "A classic of Russian literature, this novel in verse follows the story of Eugene Onegin, a bored aristocrat who rejects the love of Tatyana and later comes to regret it.",
|
||
createdBy: admin.id
|
||
});
|
||
|
||
this.addTagToWork(eugeneOnegin.id, romanticismTag.id);
|
||
this.addTagToWork(eugeneOnegin.id, novelTag.id);
|
||
this.addTagToWork(eugeneOnegin.id, verseTag.id);
|
||
|
||
// Add translations for Eugene Onegin directly
|
||
console.log("Adding translations for Eugene Onegin with ID:", eugeneOnegin.id);
|
||
|
||
// Create translations directly
|
||
const englishTransId = this.translationId++;
|
||
const englishTrans: Translation = {
|
||
id: englishTransId,
|
||
workId: eugeneOnegin.id,
|
||
translatorId: translator.id,
|
||
language: "English",
|
||
year: 1964,
|
||
title: "Eugene Onegin (Nabokov Translation)",
|
||
content: "Sample content of Nabokov's English translation of Eugene Onegin...",
|
||
description: "Vladimir Nabokov's renowned literal translation that focuses on precision and accuracy at the expense of poetic form.",
|
||
createdAt: new Date()
|
||
};
|
||
this.translations.set(englishTransId, englishTrans);
|
||
|
||
const modernEnglishTransId = this.translationId++;
|
||
const modernEnglishTrans: Translation = {
|
||
id: modernEnglishTransId,
|
||
workId: eugeneOnegin.id,
|
||
translatorId: scholar.id,
|
||
language: "English",
|
||
year: 2008,
|
||
title: "Eugene Onegin (Modern Translation)",
|
||
content: "Sample content of a modern English translation of Eugene Onegin...",
|
||
description: "A modern translation that attempts to balance accuracy with poetic rhythm and flow.",
|
||
createdAt: new Date()
|
||
};
|
||
this.translations.set(modernEnglishTransId, modernEnglishTrans);
|
||
|
||
const frenchTransId = this.translationId++;
|
||
const frenchTrans: Translation = {
|
||
id: frenchTransId,
|
||
workId: eugeneOnegin.id,
|
||
translatorId: translator.id,
|
||
language: "French",
|
||
year: 1960,
|
||
title: "Eugène Onéguine",
|
||
content: "Sample content of French translation of Eugene Onegin...",
|
||
description: "A French translation that captures the spirit and elegance of Pushkin's original verse novel.",
|
||
createdAt: new Date()
|
||
};
|
||
this.translations.set(frenchTransId, frenchTrans);
|
||
|
||
console.log("Added translations with IDs:", englishTransId, modernEnglishTransId, frenchTransId);
|
||
|
||
const bronzeHorseman = this.createWork({
|
||
title: "The Bronze Horseman",
|
||
slug: "the-bronze-horseman",
|
||
authorId: pushkin.id,
|
||
year: 1833,
|
||
type: "poem",
|
||
language: "Russian",
|
||
content: "Sample content for The Bronze Horseman...",
|
||
description: "A narrative poem depicting the equestrian statue of Peter the Great in Saint Petersburg and the great flood of 1824, widely considered one of Pushkin's masterpieces.",
|
||
createdBy: admin.id
|
||
});
|
||
|
||
this.addTagToWork(bronzeHorseman.id, poetryTag.id);
|
||
this.addTagToWork(bronzeHorseman.id, historyTag.id);
|
||
|
||
const queenOfSpades = this.createWork({
|
||
title: "The Queen of Spades",
|
||
slug: "the-queen-of-spades",
|
||
authorId: pushkin.id,
|
||
year: 1834,
|
||
type: "story",
|
||
language: "Russian",
|
||
content: "Sample content for The Queen of Spades...",
|
||
description: "A short story about human avarice, telling the tale of a young officer seeking the secret of an elderly countess's success at faro, a card game.",
|
||
createdBy: admin.id
|
||
});
|
||
|
||
this.addTagToWork(queenOfSpades.id, fictionTag.id);
|
||
this.addTagToWork(queenOfSpades.id, gothicTag.id);
|
||
|
||
// Create sample translations
|
||
this.createTranslation({
|
||
workId: lovedYouWork.id,
|
||
title: "I Loved You",
|
||
language: "English",
|
||
content: "I loved you, and I probably still do,\nAnd for a while the feeling may remain...\nBut let my love no longer trouble you,\nI do not wish to cause you any pain.\nI loved you silently, without hope,\nAt times too jealous and at times too shy.\nI loved you truly, with a tender scope,\nAs may God grant you to be loved now by.",
|
||
translatorId: admin.id,
|
||
year: 2018,
|
||
notes: "Translated by John Fenwick in 2018. This translation attempts to preserve the rhyme scheme (ABAB CDCD) and structure of the original while conveying the tender sentiment of Pushkin's lyric."
|
||
});
|
||
|
||
// Create timeline events
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1799,
|
||
title: "Birth",
|
||
description: "Born in Moscow to Sergei Lvovich and Nadezhda Ossipovna Pushkin"
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1811,
|
||
title: "Education",
|
||
description: "Enrolled at the Imperial Lyceum at Tsarskoye Selo"
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1820,
|
||
title: "First major work",
|
||
description: "Published his first long poem 'Ruslan and Ludmila'"
|
||
});
|
||
|
||
this.createTimelineEvent({
|
||
authorId: pushkin.id,
|
||
year: 1837,
|
||
title: "Death",
|
||
description: "Died in St. Petersburg from wounds received in a duel with Georges d'Anthès"
|
||
});
|
||
|
||
// Create sample blog post - work around async issues by creating the object directly
|
||
const blogPostId = this.blogPostId++;
|
||
const blogPost: BlogPost = {
|
||
id: blogPostId,
|
||
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: admin.id,
|
||
excerpt: "Exploring how digital platforms can help preserve and promote the rich tradition of Russian poetry in the modern age.",
|
||
publishedAt: new Date(),
|
||
createdAt: new Date()
|
||
};
|
||
|
||
// Add the blog post to storage manually to avoid async issues
|
||
this.blogPosts.set(blogPostId, blogPost);
|
||
console.log('Created blog post with ID:', blogPostId);
|
||
|
||
// Add tags to the blog post with explicit tag IDs
|
||
console.log('Adding tags to blog post', blogPostId);
|
||
const blogPoetryTagId = this.tags.get(3)?.id; // 3 is typically the ID for poetryTag
|
||
const blogRomanticismTagId = this.tags.get(4)?.id; // 4 is typically the ID for romanticismTag
|
||
|
||
if (blogPoetryTagId) {
|
||
this.addTagToBlogPost(blogPostId, blogPoetryTagId);
|
||
}
|
||
|
||
if (blogRomanticismTagId) {
|
||
this.addTagToBlogPost(blogPostId, blogRomanticismTagId);
|
||
}
|
||
|
||
// Add a comment to the blog post
|
||
console.log('Creating comment for blog post', blogPostId);
|
||
this.createComment({
|
||
content: "Fascinating exploration of how technology can preserve literary traditions!",
|
||
userId: admin.id,
|
||
workId: null as any, // TypeScript workaround for optional field
|
||
translationId: null,
|
||
lineNumber: null,
|
||
entityType: "blogPost",
|
||
entityId: blogPostId,
|
||
parentId: null
|
||
});
|
||
}
|
||
|
||
// User methods
|
||
async getUser(id: number): Promise<User | undefined> {
|
||
return this.users.get(id);
|
||
}
|
||
|
||
async getUserByUsername(username: string): Promise<User | undefined> {
|
||
return Array.from(this.users.values()).find(user => user.username === username);
|
||
}
|
||
|
||
async createUser(insertUser: InsertUser): Promise<User> {
|
||
const id = this.userId++;
|
||
const now = new Date();
|
||
const user: User = {
|
||
...insertUser,
|
||
id,
|
||
role: 'user',
|
||
createdAt: now
|
||
};
|
||
this.users.set(id, user);
|
||
return user;
|
||
}
|
||
|
||
// Author methods
|
||
async getAuthors(limit: number = 100, offset: number = 0): Promise<Author[]> {
|
||
return Array.from(this.authors.values())
|
||
.sort((a, b) => a.name.localeCompare(b.name))
|
||
.slice(offset, offset + limit);
|
||
}
|
||
|
||
async getAuthorById(id: number): Promise<Author | undefined> {
|
||
return this.authors.get(id);
|
||
}
|
||
|
||
async getAuthorBySlug(slug: string): Promise<Author | undefined> {
|
||
return Array.from(this.authors.values()).find(author => author.slug === slug);
|
||
}
|
||
|
||
async createAuthor(insertAuthor: InsertAuthor): Promise<Author> {
|
||
const id = this.authorId++;
|
||
const now = new Date();
|
||
const author: Author = {
|
||
...insertAuthor,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.authors.set(id, author);
|
||
return author;
|
||
}
|
||
|
||
// Work methods
|
||
async getWorks(limit: number = 100, offset: number = 0): Promise<Work[]> {
|
||
return Array.from(this.works.values())
|
||
.sort((a, b) => a.title.localeCompare(b.title))
|
||
.slice(offset, offset + limit);
|
||
}
|
||
|
||
async getWorkById(id: number): Promise<Work | undefined> {
|
||
return this.works.get(id);
|
||
}
|
||
|
||
async getWorkBySlug(slug: string): Promise<Work | undefined> {
|
||
return Array.from(this.works.values()).find(work => work.slug === slug);
|
||
}
|
||
|
||
async getWorksByAuthorId(authorId: number): Promise<Work[]> {
|
||
return Array.from(this.works.values()).filter(work => work.authorId === authorId);
|
||
}
|
||
|
||
async getWorksByTags(tagIds: number[]): Promise<Work[]> {
|
||
const tagSet = new Set(tagIds);
|
||
const workIds = new Set<number>();
|
||
|
||
this.workTagsRelations.forEach((tags, workId) => {
|
||
for (const tagId of tags) {
|
||
if (tagSet.has(tagId)) {
|
||
workIds.add(workId);
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
|
||
return Array.from(workIds).map(id => this.works.get(id)!).filter(Boolean);
|
||
}
|
||
|
||
async createWork(insertWork: InsertWork): Promise<Work> {
|
||
const id = this.workId++;
|
||
const now = new Date();
|
||
const work: Work = {
|
||
...insertWork,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.works.set(id, work);
|
||
return work;
|
||
}
|
||
|
||
// Translation methods
|
||
async getTranslations(workId: number): Promise<Translation[]> {
|
||
return Array.from(this.translations.values()).filter(
|
||
translation => translation.workId === workId
|
||
);
|
||
}
|
||
|
||
async getTranslationById(id: number): Promise<Translation | undefined> {
|
||
return this.translations.get(id);
|
||
}
|
||
|
||
async createTranslation(insertTranslation: InsertTranslation): Promise<Translation> {
|
||
const id = this.translationId++;
|
||
const now = new Date();
|
||
const translation: Translation = {
|
||
...insertTranslation,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.translations.set(id, translation);
|
||
return translation;
|
||
}
|
||
|
||
// Tag methods
|
||
async getTags(): Promise<Tag[]> {
|
||
return Array.from(this.tags.values());
|
||
}
|
||
|
||
async getTagsByType(type: string): Promise<Tag[]> {
|
||
return Array.from(this.tags.values()).filter(tag => tag.type === type);
|
||
}
|
||
|
||
async createTag(insertTag: InsertTag): Promise<Tag> {
|
||
const id = this.tagId++;
|
||
const now = new Date();
|
||
const tag: Tag = {
|
||
...insertTag,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.tags.set(id, tag);
|
||
return tag;
|
||
}
|
||
|
||
async addTagToWork(workId: number, tagId: number): Promise<void> {
|
||
if (!this.workTagsRelations.has(workId)) {
|
||
this.workTagsRelations.set(workId, new Set());
|
||
}
|
||
this.workTagsRelations.get(workId)!.add(tagId);
|
||
}
|
||
|
||
async getWorkTags(workId: number): Promise<Tag[]> {
|
||
const tagIds = this.workTagsRelations.get(workId) || new Set();
|
||
return Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
|
||
}
|
||
|
||
async getBlogPostTags(postId: number): Promise<Tag[]> {
|
||
console.log(`Getting tags for blog post ${postId}`);
|
||
console.log(`BlogPostTagsRelations map:`, Array.from(this.blogPostTagsRelations.entries()));
|
||
|
||
const tagIds = this.blogPostTagsRelations.get(postId) || new Set();
|
||
console.log(`Tag IDs for blog post ${postId}:`, Array.from(tagIds));
|
||
|
||
const tags = Array.from(tagIds).map(id => this.tags.get(id)!).filter(Boolean);
|
||
console.log(`Found ${tags.length} tags for blog post ${postId}:`, tags);
|
||
|
||
return tags;
|
||
}
|
||
|
||
async addTagToBlogPost(postId: number, tagId: number): Promise<void> {
|
||
console.log(`Adding tag ${tagId} to blog post ${postId}`);
|
||
|
||
if (!this.blogPostTagsRelations.has(postId)) {
|
||
console.log(`Creating new tag set for blog post ${postId}`);
|
||
this.blogPostTagsRelations.set(postId, new Set());
|
||
}
|
||
|
||
this.blogPostTagsRelations.get(postId)!.add(tagId);
|
||
console.log(`After adding tag, blog post ${postId} has tags:`,
|
||
Array.from(this.blogPostTagsRelations.get(postId) || new Set()));
|
||
}
|
||
|
||
// Bookmark methods
|
||
async getBookmarksByUserId(userId: number): Promise<Bookmark[]> {
|
||
return Array.from(this.bookmarks.values()).filter(
|
||
bookmark => bookmark.userId === userId
|
||
);
|
||
}
|
||
|
||
async getBookmarkByUserAndWork(userId: number, workId: number): Promise<Bookmark | undefined> {
|
||
return Array.from(this.bookmarks.values()).find(
|
||
bookmark => bookmark.userId === userId && bookmark.workId === workId
|
||
);
|
||
}
|
||
|
||
async createBookmark(insertBookmark: InsertBookmark): Promise<Bookmark> {
|
||
const id = this.bookmarkId++;
|
||
const now = new Date();
|
||
const bookmark: Bookmark = {
|
||
...insertBookmark,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.bookmarks.set(id, bookmark);
|
||
return bookmark;
|
||
}
|
||
|
||
async deleteBookmark(id: number): Promise<void> {
|
||
this.bookmarks.delete(id);
|
||
}
|
||
|
||
// Comment methods
|
||
async getCommentsByWorkId(workId: number): Promise<Comment[]> {
|
||
return Array.from(this.comments.values()).filter(
|
||
comment => comment.workId === workId
|
||
);
|
||
}
|
||
|
||
async getCommentsByTranslationId(translationId: number): Promise<Comment[]> {
|
||
return Array.from(this.comments.values()).filter(
|
||
comment => comment.translationId === translationId
|
||
);
|
||
}
|
||
|
||
async getCommentsByUserId(userId: number): Promise<Comment[]> {
|
||
return Array.from(this.comments.values()).filter(
|
||
comment => comment.userId === userId
|
||
);
|
||
}
|
||
|
||
async getCommentsByEntityId(entityType: string, entityId: number): Promise<Comment[]> {
|
||
console.log(`Looking for comments with entityType=${entityType} and entityId=${entityId}`);
|
||
|
||
// If we're using the primary entity fields (workId or translationId)
|
||
if (entityType === 'work') {
|
||
return this.getCommentsByWorkId(entityId);
|
||
} else if (entityType === 'translation') {
|
||
return this.getCommentsByTranslationId(entityId);
|
||
} else {
|
||
// For all other entity types, use the generic entityType and entityId fields
|
||
const comments = Array.from(this.comments.values()).filter(
|
||
comment =>
|
||
comment.entityType === entityType &&
|
||
comment.entityId === entityId
|
||
);
|
||
|
||
console.log(`Found ${comments.length} comments for ${entityType}:${entityId}`, comments);
|
||
return comments;
|
||
}
|
||
}
|
||
|
||
async createComment(insertComment: InsertComment): Promise<Comment> {
|
||
const id = this.commentId++;
|
||
const now = new Date();
|
||
const comment: Comment = {
|
||
...insertComment,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.comments.set(id, comment);
|
||
return comment;
|
||
}
|
||
|
||
// Like methods
|
||
async createLike(insertLike: InsertLike): Promise<Like> {
|
||
const id = this.likeId++;
|
||
const now = new Date();
|
||
const like: Like = {
|
||
...insertLike,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.likes.set(id, like);
|
||
return like;
|
||
}
|
||
|
||
async deleteLike(id: number): Promise<void> {
|
||
this.likes.delete(id);
|
||
}
|
||
|
||
async getLikesByEntity(entityType: string, entityId: number): Promise<Like[]> {
|
||
return Array.from(this.likes.values()).filter(
|
||
like => like.entityType === entityType && like.entityId === entityId
|
||
);
|
||
}
|
||
|
||
async getLikesByUserId(userId: number): Promise<Like[]> {
|
||
return Array.from(this.likes.values()).filter(
|
||
like => like.userId === userId
|
||
);
|
||
}
|
||
|
||
// Collection methods
|
||
async getCollections(limit: number = 100, offset: number = 0): Promise<Collection[]> {
|
||
return Array.from(this.collections.values())
|
||
.filter(collection => collection.isPublic)
|
||
.sort((a, b) => a.name.localeCompare(b.name))
|
||
.slice(offset, offset + limit);
|
||
}
|
||
|
||
async getCollectionsByUserId(userId: number): Promise<Collection[]> {
|
||
return Array.from(this.collections.values()).filter(
|
||
collection => collection.userId === userId
|
||
);
|
||
}
|
||
|
||
async getCollectionById(id: number): Promise<Collection | undefined> {
|
||
return this.collections.get(id);
|
||
}
|
||
|
||
async getCollectionBySlug(slug: string): Promise<Collection | undefined> {
|
||
return Array.from(this.collections.values()).find(
|
||
collection => collection.slug === slug
|
||
);
|
||
}
|
||
|
||
async createCollection(insertCollection: InsertCollection): Promise<Collection> {
|
||
const id = this.collectionId++;
|
||
const now = new Date();
|
||
const collection: Collection = {
|
||
...insertCollection,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.collections.set(id, collection);
|
||
return collection;
|
||
}
|
||
|
||
async addWorkToCollection(insertCollectionItem: InsertCollectionItem): Promise<CollectionItem> {
|
||
const id = this.collectionItemId++;
|
||
const now = new Date();
|
||
const collectionItem: CollectionItem = {
|
||
...insertCollectionItem,
|
||
id,
|
||
addedAt: now
|
||
};
|
||
this.collectionItems.set(id, collectionItem);
|
||
return collectionItem;
|
||
}
|
||
|
||
async getCollectionItems(collectionId: number): Promise<CollectionItem[]> {
|
||
return Array.from(this.collectionItems.values())
|
||
.filter(item => item.collectionId === collectionId)
|
||
.sort((a, b) => a.order - b.order);
|
||
}
|
||
|
||
// Timeline events
|
||
async getTimelineEventsByAuthorId(authorId: number): Promise<TimelineEvent[]> {
|
||
return Array.from(this.timelineEvents.values())
|
||
.filter(event => event.authorId === authorId)
|
||
.sort((a, b) => a.year - b.year);
|
||
}
|
||
|
||
async createTimelineEvent(insertEvent: InsertTimelineEvent): Promise<TimelineEvent> {
|
||
const id = this.timelineEventId++;
|
||
const now = new Date();
|
||
const event: TimelineEvent = {
|
||
...insertEvent,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.timelineEvents.set(id, event);
|
||
return event;
|
||
}
|
||
|
||
// Blog posts
|
||
async getBlogPosts(limit: number = 100, offset: number = 0): Promise<BlogPost[]> {
|
||
return Array.from(this.blogPosts.values())
|
||
.filter(post => post.publishedAt !== null)
|
||
.sort((a, b) => (b.publishedAt?.getTime() || 0) - (a.publishedAt?.getTime() || 0))
|
||
.slice(offset, offset + limit);
|
||
}
|
||
|
||
async getBlogPostById(id: number): Promise<BlogPost | undefined> {
|
||
return this.blogPosts.get(id);
|
||
}
|
||
|
||
async getBlogPostBySlug(slug: string): Promise<BlogPost | undefined> {
|
||
return Array.from(this.blogPosts.values()).find(post => post.slug === slug);
|
||
}
|
||
|
||
async createBlogPost(insertPost: InsertBlogPost): Promise<BlogPost> {
|
||
const id = this.blogPostId++;
|
||
const now = new Date();
|
||
const post: BlogPost = {
|
||
...insertPost,
|
||
id,
|
||
createdAt: now
|
||
};
|
||
this.blogPosts.set(id, post);
|
||
return post;
|
||
}
|
||
|
||
// Reading progress
|
||
async getReadingProgress(userId: number, workId: number, translationId?: number): Promise<ReadingProgress | undefined> {
|
||
const key = `${userId}-${workId}-${translationId || 0}`;
|
||
return this.readingProgresses.get(key);
|
||
}
|
||
|
||
async updateReadingProgress(insertProgress: InsertReadingProgress): Promise<ReadingProgress> {
|
||
const key = `${insertProgress.userId}-${insertProgress.workId}-${insertProgress.translationId || 0}`;
|
||
const now = new Date();
|
||
|
||
const existing = this.readingProgresses.get(key);
|
||
|
||
if (existing) {
|
||
const updated: ReadingProgress = {
|
||
...existing,
|
||
progress: insertProgress.progress,
|
||
lastReadAt: now
|
||
};
|
||
this.readingProgresses.set(key, updated);
|
||
return updated;
|
||
} else {
|
||
const id = this.readingProgressId++;
|
||
const progress: ReadingProgress = {
|
||
...insertProgress,
|
||
id,
|
||
lastReadAt: now
|
||
};
|
||
this.readingProgresses.set(key, progress);
|
||
return progress;
|
||
}
|
||
}
|
||
|
||
// Search and filter methods
|
||
async searchWorks(query: string, limit: number = 10): Promise<Work[]> {
|
||
const lowerQuery = query.toLowerCase();
|
||
return Array.from(this.works.values())
|
||
.filter(work =>
|
||
work.title.toLowerCase().includes(lowerQuery) ||
|
||
work.description?.toLowerCase().includes(lowerQuery)
|
||
)
|
||
.slice(0, limit);
|
||
}
|
||
|
||
async searchAuthors(query: string, limit: number = 10): Promise<Author[]> {
|
||
const lowerQuery = query.toLowerCase();
|
||
return Array.from(this.authors.values())
|
||
.filter(author =>
|
||
author.name.toLowerCase().includes(lowerQuery) ||
|
||
author.biography?.toLowerCase().includes(lowerQuery)
|
||
)
|
||
.slice(0, limit);
|
||
}
|
||
|
||
async filterWorks(params: {
|
||
language?: string,
|
||
type?: string,
|
||
yearStart?: number,
|
||
yearEnd?: number,
|
||
tags?: number[]
|
||
}): Promise<Work[]> {
|
||
let filtered = Array.from(this.works.values());
|
||
|
||
if (params.language) {
|
||
filtered = filtered.filter(work => work.language === params.language);
|
||
}
|
||
|
||
if (params.type) {
|
||
filtered = filtered.filter(work => work.type === params.type);
|
||
}
|
||
|
||
if (params.yearStart) {
|
||
filtered = filtered.filter(work => work.year !== undefined && work.year >= (params.yearStart || 0));
|
||
}
|
||
|
||
if (params.yearEnd) {
|
||
filtered = filtered.filter(work => work.year !== undefined && work.year <= (params.yearEnd || 3000));
|
||
}
|
||
|
||
if (params.tags && params.tags.length > 0) {
|
||
const taggedWorks = await this.getWorksByTags(params.tags);
|
||
const taggedWorkIds = new Set(taggedWorks.map(work => work.id));
|
||
filtered = filtered.filter(work => taggedWorkIds.has(work.id));
|
||
}
|
||
|
||
return filtered;
|
||
}
|
||
}
|
||
|
||
export const storage = new MemStorage();
|