refactor post model
This commit is contained in:
parent
62f9de9546
commit
30025b4044
8 changed files with 373 additions and 151 deletions
|
@ -2,10 +2,10 @@ import { Temporal } from '@js-temporal/polyfill'
|
|||
import { components } from '../../api/schema.ts'
|
||||
import { immerable } from 'immer'
|
||||
|
||||
export interface EmojiReaction {
|
||||
export interface PostReaction {
|
||||
emoji: string
|
||||
count: number
|
||||
didReact: boolean
|
||||
authorName: string
|
||||
reactedOn: Temporal.Instant
|
||||
}
|
||||
|
||||
export class Post {
|
||||
|
@ -16,7 +16,7 @@ export class Post {
|
|||
public readonly media: PostMedia[]
|
||||
public readonly createdAt: Temporal.Instant
|
||||
public readonly authorName: string
|
||||
public readonly reactions: EmojiReaction[]
|
||||
public readonly reactions: PostReaction[]
|
||||
public readonly possibleReactions: string[]
|
||||
|
||||
constructor(
|
||||
|
@ -25,7 +25,7 @@ export class Post {
|
|||
media: PostMedia[],
|
||||
createdAt: string | Temporal.Instant,
|
||||
authorName: string,
|
||||
reactions: EmojiReaction[] = [],
|
||||
reactions: PostReaction[] = [],
|
||||
possibleReactions: string[] = [],
|
||||
) {
|
||||
this.postId = postId
|
||||
|
@ -44,11 +44,7 @@ export class Post {
|
|||
dto.media.map((m) => new PostMediaImpl(new URL(m.url), m.width, m.height)),
|
||||
Temporal.Instant.from(dto.createdAt),
|
||||
dto.author.username,
|
||||
dto.reactions.map((r) => ({
|
||||
emoji: r.emoji,
|
||||
count: r.count,
|
||||
didReact: r.didReact,
|
||||
})),
|
||||
dto.reactions.map((r) => ({ ...r, reactedOn: Temporal.Instant.from(r.reactedOn) })),
|
||||
dto.possibleReactions,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -29,22 +29,34 @@ export class PostsService {
|
|||
return Post.fromDto(response.data.post)
|
||||
}
|
||||
|
||||
async loadPublicFeed(
|
||||
cursor: string | null,
|
||||
amount: number | null,
|
||||
): Promise<{ posts: Post[]; next: string | null }> {
|
||||
async load(postId: string): Promise<Post | null> {
|
||||
const response = await this.client.GET('/posts/{postId}', {
|
||||
params: {
|
||||
path: { postId },
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (!response.data?.post) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Post.fromDto(response.data.post)
|
||||
}
|
||||
|
||||
async loadPublicFeed(cursor: string | null, amount: number | null): Promise<{ posts: Post[] }> {
|
||||
const response = await this.client.GET('/posts', {
|
||||
params: {
|
||||
query: { From: cursor ?? undefined, Amount: amount ?? undefined },
|
||||
query: { After: cursor ?? undefined, Amount: amount ?? undefined },
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (!response.data) {
|
||||
return { posts: [], next: null }
|
||||
return { posts: [] }
|
||||
}
|
||||
|
||||
return { posts: response.data.posts.map(Post.fromDto), next: response.data.next }
|
||||
return { posts: response.data.posts.map(Post.fromDto) }
|
||||
}
|
||||
|
||||
async addReaction(postId: string, emoji: string): Promise<void> {
|
||||
|
|
82
src/app/feed/posts/usePostViewModel.ts
Normal file
82
src/app/feed/posts/usePostViewModel.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import { Post, PostMedia, PostReaction } from './posts.ts'
|
||||
import { Temporal } from '@js-temporal/polyfill'
|
||||
import { produce } from 'immer'
|
||||
|
||||
export interface PostInfo {
|
||||
postId: string
|
||||
authorName: string
|
||||
content: string
|
||||
createdAt: Temporal.Instant
|
||||
media: PostMedia[]
|
||||
possibleReactions: string[]
|
||||
}
|
||||
|
||||
type ReactionMap = Record<string, PostReaction[]>
|
||||
|
||||
export function usePostViewModel() {
|
||||
const [posts, _setPosts] = useState<PostInfo[]>([])
|
||||
const [reactions, setReactions] = useState<ReactionMap>({})
|
||||
|
||||
const setPosts = useCallback((posts: Post[]) => {
|
||||
_setPosts([...posts])
|
||||
|
||||
setReactions(
|
||||
posts.reduce((acc, post) => {
|
||||
acc[post.postId] = [...post.reactions]
|
||||
return acc
|
||||
}, {} as ReactionMap),
|
||||
)
|
||||
}, [])
|
||||
|
||||
const addPosts = useCallback((posts: Post[]) => {
|
||||
_setPosts((current) => {
|
||||
return [...current, ...posts]
|
||||
})
|
||||
|
||||
setReactions((current) =>
|
||||
produce(current, (draft) => {
|
||||
for (const post of posts) {
|
||||
draft[post.postId] = [...post.reactions]
|
||||
}
|
||||
}),
|
||||
)
|
||||
}, [])
|
||||
|
||||
function addReaction(
|
||||
postId: string,
|
||||
emoji: string,
|
||||
authorName: string,
|
||||
reactedOn: Temporal.Instant,
|
||||
) {
|
||||
setReactions((current) =>
|
||||
produce(current, (draft) => {
|
||||
if (draft[postId]?.some((r) => r.emoji === emoji && r.authorName == authorName)) {
|
||||
return
|
||||
}
|
||||
|
||||
const reaction: PostReaction = { emoji, authorName, reactedOn }
|
||||
|
||||
if (!draft[postId]) {
|
||||
draft[postId] = [{ ...reaction }]
|
||||
} else {
|
||||
draft[postId].push({ ...reaction })
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function removeReaction(postId: string, emoji: string, authorName: string) {
|
||||
setReactions((current) =>
|
||||
produce(current, (draft) => {
|
||||
if (!draft[postId]) return
|
||||
|
||||
draft[postId] = draft[postId].filter(
|
||||
(r) => r.emoji !== emoji || r.authorName !== authorName,
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return { posts, reactions, addPosts, setPosts, addReaction, removeReaction }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue