123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- /*
- eslint-disable @typescript-eslint/naming-convention
- */
- import { EventContext, StoreContext, DatabaseManager } from '@dzlzv/hydra-common'
- import {
- bytesToString,
- deserializeMetadata,
- genericEventFields,
- getWorker,
- inconsistentState,
- perpareString,
- } from './common'
- import {
- CategoryCreatedEvent,
- CategoryStatusActive,
- CategoryArchivalStatusUpdatedEvent,
- ForumCategory,
- Worker,
- CategoryStatusArchived,
- CategoryDeletedEvent,
- CategoryStatusRemoved,
- ThreadCreatedEvent,
- ForumThread,
- Membership,
- ThreadStatusActive,
- ForumPoll,
- ForumPollAlternative,
- ThreadModeratedEvent,
- ThreadStatusModerated,
- ThreadMetadataUpdatedEvent,
- ThreadDeletedEvent,
- ThreadStatusLocked,
- ThreadStatusRemoved,
- ThreadMovedEvent,
- ForumPost,
- PostStatusActive,
- PostOriginThreadInitial,
- VoteOnPollEvent,
- PostAddedEvent,
- PostStatusLocked,
- PostOriginThreadReply,
- CategoryStickyThreadUpdateEvent,
- CategoryMembershipOfModeratorUpdatedEvent,
- PostModeratedEvent,
- PostStatusModerated,
- ForumPostReaction,
- PostReaction,
- PostReactedEvent,
- PostReactionResult,
- PostReactionResultCancel,
- PostReactionResultValid,
- PostReactionResultInvalid,
- PostTextUpdatedEvent,
- PostDeletedEvent,
- PostStatusRemoved,
- ForumThreadTag,
- } from 'query-node/dist/model'
- import { Forum } from './generated/types'
- import { PostReactionId, PrivilegedActor } from '@joystream/types/augment/all'
- import {
- ForumPostMetadata,
- ForumPostReaction as SupportedPostReactions,
- ForumThreadMetadata,
- } from '@joystream/metadata-protobuf'
- import { isSet } from '@joystream/metadata-protobuf/utils'
- import { MAX_TAGS_PER_FORUM_THREAD } from '@joystream/metadata-protobuf/consts'
- import { Not, In } from 'typeorm'
- import { Bytes } from '@polkadot/types'
- import _ from 'lodash'
- async function getCategory(store: DatabaseManager, categoryId: string, relations?: string[]): Promise<ForumCategory> {
- const category = await store.get(ForumCategory, { where: { id: categoryId }, relations })
- if (!category) {
- throw new Error(`Forum category not found by id: ${categoryId}`)
- }
- return category
- }
- async function getThread(store: DatabaseManager, threadId: string): Promise<ForumThread> {
- const thread = await store.get(ForumThread, { where: { id: threadId } })
- if (!thread) {
- throw new Error(`Forum thread not found by id: ${threadId.toString()}`)
- }
- return thread
- }
- async function getPost(store: DatabaseManager, postId: string, relations?: 'thread'[]): Promise<ForumPost> {
- const post = await store.get(ForumPost, { where: { id: postId }, relations })
- if (!post) {
- throw new Error(`Forum post not found by id: ${postId.toString()}`)
- }
- return post
- }
- async function getPollAlternative(store: DatabaseManager, threadId: string, index: number) {
- const poll = await store.get(ForumPoll, { where: { thread: { id: threadId } }, relations: ['pollAlternatives'] })
- if (!poll) {
- throw new Error(`Forum poll not found by threadId: ${threadId.toString()}`)
- }
- const pollAlternative = poll.pollAlternatives?.find((alt) => alt.index === index)
- if (!pollAlternative) {
- throw new Error(`Froum poll alternative not found by index ${index} in thread ${threadId.toString()}`)
- }
- return pollAlternative
- }
- async function getActorWorker(store: DatabaseManager, actor: PrivilegedActor): Promise<Worker> {
- const worker = await store.get(Worker, {
- where: {
- group: { id: 'forumWorkingGroup' },
- ...(actor.isLead ? { isLead: true } : { runtimeId: actor.asModerator.toNumber() }),
- },
- relations: ['group'],
- })
- if (!worker) {
- throw new Error(`Corresponding worker not found by forum PrivielagedActor: ${JSON.stringify(actor.toHuman())}`)
- }
- return worker
- }
- function normalizeForumTagLabel(label: string): string {
- // Optionally: normalize to lowercase & ASCII only?
- return perpareString(label)
- }
- function parseThreadMetadata(metaBytes: Bytes) {
- const meta = deserializeMetadata(ForumThreadMetadata, metaBytes)
- return {
- title: meta ? meta.title : bytesToString(metaBytes),
- tags:
- meta && isSet(meta.tags)
- ? _.uniq(meta.tags.slice(0, MAX_TAGS_PER_FORUM_THREAD).map((label) => normalizeForumTagLabel(label))).filter(
- (v) => v // Filter out empty strings
- )
- : undefined,
- }
- }
- async function prepareThreadTagsToSet(
- { event, store }: StoreContext & EventContext,
- labels: string[]
- ): Promise<ForumThreadTag[]> {
- const eventTime = new Date(event.blockTimestamp)
- return Promise.all(
- labels.map(async (label) => {
- const forumTag =
- (await store.get(ForumThreadTag, { where: { id: label } })) ||
- new ForumThreadTag({
- id: label,
- createdAt: eventTime,
- visibleThreadsCount: 0,
- })
- forumTag.updatedAt = eventTime
- ++forumTag.visibleThreadsCount
- await store.save<ForumThreadTag>(forumTag)
- return forumTag
- })
- )
- }
- async function unsetThreadTags({ event, store }: StoreContext & EventContext, tags: ForumThreadTag[]): Promise<void> {
- const eventTime = new Date(event.blockTimestamp)
- await Promise.all(
- tags.map(async (forumTag) => {
- --forumTag.visibleThreadsCount
- if (forumTag.visibleThreadsCount < 0) {
- inconsistentState('Trying to update forumTag.visibleThreadsCount to a number below 0!')
- }
- forumTag.updatedAt = eventTime
- await store.save<ForumThreadTag>(forumTag)
- })
- )
- }
- // Get standarized PostReactionResult by PostReactionId
- function parseReaction(reactionId: PostReactionId): typeof PostReactionResult {
- switch (reactionId.toNumber()) {
- case SupportedPostReactions.Reaction.CANCEL: {
- return new PostReactionResultCancel()
- }
- case SupportedPostReactions.Reaction.LIKE: {
- const result = new PostReactionResultValid()
- result.reaction = PostReaction.LIKE
- result.reactionId = reactionId.toNumber()
- return result
- }
- default: {
- console.warn(`Invalid post reaction id: ${reactionId.toString()}`)
- const result = new PostReactionResultInvalid()
- result.reactionId = reactionId.toNumber()
- return result
- }
- }
- }
- export async function forum_CategoryCreated({ event, store }: EventContext & StoreContext): Promise<void> {
- const [categoryId, parentCategoryId, titleBytes, descriptionBytes] = new Forum.CategoryCreatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const category = new ForumCategory({
- id: categoryId.toString(),
- createdAt: eventTime,
- updatedAt: eventTime,
- title: bytesToString(titleBytes),
- description: bytesToString(descriptionBytes),
- status: new CategoryStatusActive(),
- parent: parentCategoryId.isSome ? new ForumCategory({ id: parentCategoryId.unwrap().toString() }) : undefined,
- })
- await store.save<ForumCategory>(category)
- const categoryCreatedEvent = new CategoryCreatedEvent({
- ...genericEventFields(event),
- category,
- })
- await store.save<CategoryCreatedEvent>(categoryCreatedEvent)
- }
- export async function forum_CategoryArchivalStatusUpdated({
- event,
- store,
- }: EventContext & StoreContext): Promise<void> {
- const [categoryId, newArchivalStatus, privilegedActor] = new Forum.CategoryArchivalStatusUpdatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const category = await getCategory(store, categoryId.toString())
- const actorWorker = await getActorWorker(store, privilegedActor)
- const categoryArchivalStatusUpdatedEvent = new CategoryArchivalStatusUpdatedEvent({
- ...genericEventFields(event),
- category,
- newArchivalStatus: newArchivalStatus.valueOf(),
- actor: actorWorker,
- })
- await store.save<CategoryArchivalStatusUpdatedEvent>(categoryArchivalStatusUpdatedEvent)
- if (newArchivalStatus.valueOf()) {
- const status = new CategoryStatusArchived()
- status.categoryArchivalStatusUpdatedEventId = categoryArchivalStatusUpdatedEvent.id
- category.status = status
- } else {
- category.status = new CategoryStatusActive()
- }
- category.updatedAt = eventTime
- await store.save<ForumCategory>(category)
- }
- export async function forum_CategoryDeleted({ event, store }: EventContext & StoreContext): Promise<void> {
- const [categoryId, privilegedActor] = new Forum.CategoryDeletedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const category = await getCategory(store, categoryId.toString())
- const actorWorker = await getActorWorker(store, privilegedActor)
- const categoryDeletedEvent = new CategoryDeletedEvent({
- ...genericEventFields(event),
- category,
- actor: actorWorker,
- })
- await store.save<CategoryDeletedEvent>(categoryDeletedEvent)
- const newStatus = new CategoryStatusRemoved()
- newStatus.categoryDeletedEventId = categoryDeletedEvent.id
- category.updatedAt = eventTime
- category.status = newStatus
- await store.save<ForumCategory>(category)
- }
- export async function forum_ThreadCreated(ctx: EventContext & StoreContext): Promise<void> {
- const { event, store } = ctx
- const [
- categoryId,
- threadId,
- postId,
- memberId,
- threadMetaBytes,
- postTextBytes,
- pollInput,
- ] = new Forum.ThreadCreatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const author = new Membership({ id: memberId.toString() })
- const { title, tags } = parseThreadMetadata(threadMetaBytes)
- const thread = new ForumThread({
- createdAt: eventTime,
- updatedAt: eventTime,
- id: threadId.toString(),
- author,
- category: new ForumCategory({ id: categoryId.toString() }),
- title: title || '',
- isSticky: false,
- status: new ThreadStatusActive(),
- visiblePostsCount: 1,
- tags: tags ? await prepareThreadTagsToSet(ctx, tags) : [],
- })
- await store.save<ForumThread>(thread)
- if (pollInput.isSome) {
- const threadPoll = new ForumPoll({
- createdAt: eventTime,
- updatedAt: eventTime,
- description: bytesToString(pollInput.unwrap().description),
- endTime: new Date(pollInput.unwrap().end_time.toNumber()),
- thread,
- })
- await store.save<ForumPoll>(threadPoll)
- await Promise.all(
- pollInput.unwrap().poll_alternatives.map(async (alt, index) => {
- const alternative = new ForumPollAlternative({
- createdAt: eventTime,
- updatedAt: eventTime,
- poll: threadPoll,
- text: bytesToString(alt.alternative_text),
- index,
- })
- await store.save<ForumPollAlternative>(alternative)
- })
- )
- }
- const threadCreatedEvent = new ThreadCreatedEvent({
- ...genericEventFields(event),
- thread,
- title: title || '',
- text: bytesToString(postTextBytes),
- })
- await store.save<ThreadCreatedEvent>(threadCreatedEvent)
- const postOrigin = new PostOriginThreadInitial()
- postOrigin.threadCreatedEventId = threadCreatedEvent.id
- const initialPost = new ForumPost({
- id: postId.toString(),
- createdAt: eventTime,
- updatedAt: eventTime,
- author,
- thread,
- text: bytesToString(postTextBytes),
- status: new PostStatusActive(),
- origin: postOrigin,
- })
- await store.save<ForumPost>(initialPost)
- }
- export async function forum_ThreadModerated(ctx: EventContext & StoreContext): Promise<void> {
- const { event, store } = ctx
- const [threadId, rationaleBytes, privilegedActor] = new Forum.ThreadModeratedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const actorWorker = await getActorWorker(store, privilegedActor)
- const thread = await getThread(store, threadId.toString())
- const threadModeratedEvent = new ThreadModeratedEvent({
- ...genericEventFields(event),
- actor: actorWorker,
- thread,
- rationale: bytesToString(rationaleBytes),
- })
- await store.save<ThreadModeratedEvent>(threadModeratedEvent)
- const newStatus = new ThreadStatusModerated()
- newStatus.threadModeratedEventId = threadModeratedEvent.id
- thread.updatedAt = eventTime
- thread.status = newStatus
- thread.visiblePostsCount = 0
- await unsetThreadTags(ctx, thread.tags || [])
- await store.save<ForumThread>(thread)
- }
- export async function forum_ThreadMetadataUpdated(ctx: EventContext & StoreContext): Promise<void> {
- const { event, store } = ctx
- const [threadId, , , newMetadataBytes] = new Forum.ThreadMetadataUpdatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const thread = await getThread(store, threadId.toString())
- const { title: newTitle, tags: newTagIds } = parseThreadMetadata(newMetadataBytes)
- // Only update tags if set
- if (isSet(newTagIds)) {
- const currentTagIds = (thread.tags || []).map((t) => t.id)
- const tagIdsToSet = _.difference(newTagIds, currentTagIds)
- const tagIdsToUnset = _.difference(currentTagIds, newTagIds)
- const newTags = await prepareThreadTagsToSet(ctx, tagIdsToSet)
- await unsetThreadTags(
- ctx,
- (thread.tags || []).filter((t) => tagIdsToUnset.includes(t.id))
- )
- thread.tags = newTags
- }
- if (isSet(newTitle)) {
- thread.title = newTitle
- }
- thread.updatedAt = eventTime
- await store.save<ForumThread>(thread)
- const threadMetadataUpdatedEvent = new ThreadMetadataUpdatedEvent({
- ...genericEventFields(event),
- thread,
- newTitle: newTitle || undefined,
- })
- await store.save<ThreadMetadataUpdatedEvent>(threadMetadataUpdatedEvent)
- }
- export async function forum_ThreadDeleted(ctx: EventContext & StoreContext): Promise<void> {
- const { event, store } = ctx
- const [threadId, , , hide] = new Forum.ThreadDeletedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const thread = await getThread(store, threadId.toString())
- const threadDeletedEvent = new ThreadDeletedEvent({
- ...genericEventFields(event),
- thread,
- })
- await store.save<ThreadDeletedEvent>(threadDeletedEvent)
- const status = hide.valueOf() ? new ThreadStatusRemoved() : new ThreadStatusLocked()
- status.threadDeletedEventId = threadDeletedEvent.id
- thread.status = status
- thread.updatedAt = eventTime
- if (hide.valueOf()) {
- thread.visiblePostsCount = 0
- await unsetThreadTags(ctx, thread.tags || [])
- }
- await store.save<ForumThread>(thread)
- }
- export async function forum_ThreadMoved({ event, store }: EventContext & StoreContext): Promise<void> {
- const [threadId, newCategoryId, privilegedActor, oldCategoryId] = new Forum.ThreadMovedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const thread = await getThread(store, threadId.toString())
- const actorWorker = await getActorWorker(store, privilegedActor)
- const threadMovedEvent = new ThreadMovedEvent({
- ...genericEventFields(event),
- thread,
- oldCategory: new ForumCategory({ id: oldCategoryId.toString() }),
- newCategory: new ForumCategory({ id: newCategoryId.toString() }),
- actor: actorWorker,
- })
- await store.save<ThreadMovedEvent>(threadMovedEvent)
- thread.updatedAt = eventTime
- thread.category = new ForumCategory({ id: newCategoryId.toString() })
- await store.save<ForumThread>(thread)
- }
- export async function forum_VoteOnPoll({ event, store }: EventContext & StoreContext): Promise<void> {
- const [threadId, alternativeIndex, forumUserId] = new Forum.VoteOnPollEvent(event).params
- const pollAlternative = await getPollAlternative(store, threadId.toString(), alternativeIndex.toNumber())
- const votingMember = new Membership({ id: forumUserId.toString() })
- const voteOnPollEvent = new VoteOnPollEvent({
- ...genericEventFields(event),
- pollAlternative,
- votingMember,
- })
- await store.save<VoteOnPollEvent>(voteOnPollEvent)
- }
- export async function forum_PostAdded({ event, store }: EventContext & StoreContext): Promise<void> {
- const [postId, forumUserId, , threadId, metadataBytes, isEditable] = new Forum.PostAddedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const thread = await getThread(store, threadId.toString())
- const metadata = deserializeMetadata(ForumPostMetadata, metadataBytes)
- const postText = metadata ? metadata.text || '' : bytesToString(metadataBytes)
- const repliesToPost =
- typeof metadata?.repliesTo === 'number' &&
- (await store.get(ForumPost, { where: { id: metadata.repliesTo.toString() } }))
- const postStatus = isEditable.valueOf() ? new PostStatusActive() : new PostStatusLocked()
- const postOrigin = new PostOriginThreadReply()
- const post = new ForumPost({
- id: postId.toString(),
- createdAt: eventTime,
- updatedAt: eventTime,
- text: postText,
- thread,
- status: postStatus,
- author: new Membership({ id: forumUserId.toString() }),
- origin: postOrigin,
- repliesTo: repliesToPost || undefined,
- })
- await store.save<ForumPost>(post)
- const postAddedEvent = new PostAddedEvent({
- ...genericEventFields(event),
- post,
- isEditable: isEditable.valueOf(),
- text: postText,
- })
- await store.save<PostAddedEvent>(postAddedEvent)
- // Update the other side of cross-relationship
- postOrigin.postAddedEventId = postAddedEvent.id
- await store.save<ForumPost>(post)
- ++thread.visiblePostsCount
- thread.updatedAt = eventTime
- await store.save<ForumThread>(thread)
- }
- export async function forum_CategoryStickyThreadUpdate({ event, store }: EventContext & StoreContext): Promise<void> {
- const [categoryId, newStickyThreadsIdsVec, privilegedActor] = new Forum.CategoryStickyThreadUpdateEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const actorWorker = await getActorWorker(store, privilegedActor)
- const newStickyThreadsIds = newStickyThreadsIdsVec.map((id) => id.toString())
- const threadsToSetSticky = await store.getMany(ForumThread, {
- where: { category: { id: categoryId.toString() }, id: In(newStickyThreadsIds) },
- })
- const threadsToUnsetSticky = await store.getMany(ForumThread, {
- where: { category: { id: categoryId.toString() }, isSticky: true, id: Not(In(newStickyThreadsIds)) },
- })
- const setStickyUpdates = (threadsToSetSticky || []).map(async (t) => {
- t.updatedAt = eventTime
- t.isSticky = true
- await store.save<ForumThread>(t)
- })
- const unsetStickyUpdates = (threadsToUnsetSticky || []).map(async (t) => {
- t.updatedAt = eventTime
- t.isSticky = false
- await store.save<ForumThread>(t)
- })
- await Promise.all(setStickyUpdates.concat(unsetStickyUpdates))
- const categoryStickyThreadUpdateEvent = new CategoryStickyThreadUpdateEvent({
- ...genericEventFields(event),
- actor: actorWorker,
- category: new ForumCategory({ id: categoryId.toString() }),
- newStickyThreads: threadsToSetSticky,
- })
- await store.save<CategoryStickyThreadUpdateEvent>(categoryStickyThreadUpdateEvent)
- }
- export async function forum_CategoryMembershipOfModeratorUpdated({
- store,
- event,
- }: EventContext & StoreContext): Promise<void> {
- const [moderatorId, categoryId, canModerate] = new Forum.CategoryMembershipOfModeratorUpdatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const moderator = await getWorker(store, 'forumWorkingGroup', moderatorId.toNumber())
- const category = await getCategory(store, categoryId.toString(), ['moderators'])
- if (canModerate.valueOf()) {
- category.moderators.push(moderator)
- category.updatedAt = eventTime
- await store.save<ForumCategory>(category)
- } else {
- category.moderators.splice(category.moderators.map((m) => m.id).indexOf(moderator.id), 1)
- category.updatedAt = eventTime
- await store.save<ForumCategory>(category)
- }
- const categoryMembershipOfModeratorUpdatedEvent = new CategoryMembershipOfModeratorUpdatedEvent({
- ...genericEventFields(event),
- category,
- moderator,
- newCanModerateValue: canModerate.valueOf(),
- })
- await store.save<CategoryMembershipOfModeratorUpdatedEvent>(categoryMembershipOfModeratorUpdatedEvent)
- }
- export async function forum_PostModerated({ event, store }: EventContext & StoreContext): Promise<void> {
- const [postId, rationaleBytes, privilegedActor] = new Forum.PostModeratedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const actorWorker = await getActorWorker(store, privilegedActor)
- const post = await getPost(store, postId.toString(), ['thread'])
- const postModeratedEvent = new PostModeratedEvent({
- ...genericEventFields(event),
- actor: actorWorker,
- post,
- rationale: bytesToString(rationaleBytes),
- })
- await store.save<PostModeratedEvent>(postModeratedEvent)
- const newStatus = new PostStatusModerated()
- newStatus.postModeratedEventId = postModeratedEvent.id
- post.updatedAt = eventTime
- post.status = newStatus
- await store.save<ForumPost>(post)
- const { thread } = post
- --thread.visiblePostsCount
- thread.updatedAt = eventTime
- await store.save<ForumThread>(thread)
- }
- export async function forum_PostReacted({ event, store }: EventContext & StoreContext): Promise<void> {
- const [userId, postId, reactionId] = new Forum.PostReactedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const reactionResult = parseReaction(reactionId)
- const postReactedEvent = new PostReactedEvent({
- ...genericEventFields(event),
- post: new ForumPost({ id: postId.toString() }),
- reactingMember: new Membership({ id: userId.toString() }),
- reactionResult,
- })
- await store.save<PostReactedEvent>(postReactedEvent)
- const existingUserPostReaction = await store.get(ForumPostReaction, {
- where: { post: { id: postId.toString() }, member: { id: userId.toString() } },
- })
- if (reactionResult.isTypeOf === 'PostReactionResultValid') {
- const { reaction } = reactionResult as PostReactionResultValid
- if (existingUserPostReaction) {
- existingUserPostReaction.updatedAt = eventTime
- existingUserPostReaction.reaction = reaction
- await store.save<ForumPostReaction>(existingUserPostReaction)
- } else {
- const newUserPostReaction = new ForumPostReaction({
- createdAt: eventTime,
- updatedAt: eventTime,
- post: new ForumPost({ id: postId.toString() }),
- member: new Membership({ id: userId.toString() }),
- reaction,
- })
- await store.save<ForumPostReaction>(newUserPostReaction)
- }
- } else if (existingUserPostReaction) {
- await store.remove<ForumPostReaction>(existingUserPostReaction)
- }
- }
- export async function forum_PostTextUpdated({ event, store }: EventContext & StoreContext): Promise<void> {
- const [postId, , , , newTextBytes] = new Forum.PostTextUpdatedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const post = await getPost(store, postId.toString())
- const postTextUpdatedEvent = new PostTextUpdatedEvent({
- ...genericEventFields(event),
- post,
- newText: bytesToString(newTextBytes),
- })
- await store.save<PostTextUpdatedEvent>(postTextUpdatedEvent)
- post.updatedAt = eventTime
- post.text = bytesToString(newTextBytes)
- await store.save<ForumPost>(post)
- }
- export async function forum_PostDeleted({ event, store }: EventContext & StoreContext): Promise<void> {
- // FIXME: Custom posts BTreeMap fix (because of invalid BTreeMap json encoding/decoding)
- // See: https://github.com/polkadot-js/api/pull/3789
- event.params[2].value = new Map(
- Object.entries(event.params[2].value).map(([key, val]) => [JSON.parse(key), val])
- ) as any
- const [rationaleBytes, userId, postsData] = new Forum.PostDeletedEvent(event).params
- const eventTime = new Date(event.blockTimestamp)
- const postDeletedEvent = new PostDeletedEvent({
- ...genericEventFields(event),
- actor: new Membership({ id: userId.toString() }),
- rationale: bytesToString(rationaleBytes),
- })
- await store.save<PostDeletedEvent>(postDeletedEvent)
- await Promise.all(
- Array.from(postsData.entries()).map(async ([[, , postId], hideFlag]) => {
- const post = await getPost(store, postId.toString(), ['thread'])
- const newStatus = hideFlag.valueOf() ? new PostStatusRemoved() : new PostStatusLocked()
- newStatus.postDeletedEventId = postDeletedEvent.id
- post.updatedAt = eventTime
- post.status = newStatus
- post.deletedInEvent = postDeletedEvent
- await store.save<ForumPost>(post)
- if (hideFlag.valueOf()) {
- const { thread } = post
- --thread.visiblePostsCount
- thread.updatedAt = eventTime
- await store.save<ForumThread>(thread)
- }
- })
- )
- }
|