use reactions from post

This commit is contained in:
john 2025-05-28 19:56:30 +02:00
parent 72389136a7
commit 83835d374b
3 changed files with 80 additions and 70 deletions

View file

@ -26,9 +26,9 @@ export interface paths {
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
'text/plain': components['schemas']['GetAllPublicPostsResponse'] 'text/plain': components['schemas']['LoadPostsResponse']
'application/json': components['schemas']['GetAllPublicPostsResponse'] 'application/json': components['schemas']['LoadPostsResponse']
'text/json': components['schemas']['GetAllPublicPostsResponse'] 'text/json': components['schemas']['LoadPostsResponse']
} }
} }
} }
@ -68,6 +68,41 @@ export interface paths {
patch?: never patch?: never
trace?: never trace?: never
} }
'/posts/{postId}': {
parameters: {
query?: never
header?: never
path?: never
cookie?: never
}
get?: never
put?: never
post?: never
delete: {
parameters: {
query?: never
header?: never
path: {
postId: string
}
cookie?: never
}
requestBody?: never
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown
}
content?: never
}
}
}
options?: never
head?: never
patch?: never
trace?: never
}
'/media': { '/media': {
parameters: { parameters: {
query?: never query?: never
@ -87,7 +122,8 @@ export interface paths {
requestBody: { requestBody: {
content: { content: {
'multipart/form-data': { 'multipart/form-data': {
file?: components['schemas']['IFormFile'] /** Format: binary */
file?: string
} }
} }
} }
@ -387,24 +423,21 @@ export interface components {
height: number | null height: number | null
} }
CreatePostResponse: { CreatePostResponse: {
/** Format: uuid */ post: components['schemas']['PostDto']
postId: string
} }
CreateSignupCodeRequest: { CreateSignupCodeRequest: {
code: string code: string
email: string email: string
name: string name: string
} }
GetAllPublicPostsResponse: { ListSignupCodesResult: {
signupCodes: components['schemas']['SignupCodeDto'][]
}
LoadPostsResponse: {
posts: components['schemas']['PostDto'][] posts: components['schemas']['PostDto'][]
/** Format: uuid */ /** Format: uuid */
next: string | null next: string | null
} }
/** Format: binary */
IFormFile: string
ListSignupCodesResult: {
signupCodes: components['schemas']['SignupCodeDto'][]
}
LoginRequest: { LoginRequest: {
username: string username: string
password: string password: string
@ -426,8 +459,10 @@ export interface components {
postId: string postId: string
content: string content: string
media: components['schemas']['PostMediaDto'][] media: components['schemas']['PostMediaDto'][]
reactions: components['schemas']['PostReactionDto'][]
/** Format: date-time */ /** Format: date-time */
createdAt: string createdAt: string
possibleReactions: string[]
} }
PostMediaDto: { PostMediaDto: {
/** Format: uri */ /** Format: uri */
@ -437,6 +472,12 @@ export interface components {
/** Format: int32 */ /** Format: int32 */
height: number | null height: number | null
} }
PostReactionDto: {
emoji: string
/** Format: int32 */
count: number
didReact: boolean
}
RefreshUserResult: { RefreshUserResult: {
/** Format: uuid */ /** Format: uuid */
userId: string userId: string

View file

@ -55,9 +55,6 @@ interface PostReactionsProps {
} }
function PostReactions({ post }: PostReactionsProps) { function PostReactions({ post }: PostReactionsProps) {
// State to track user's reactions
const [userReactions, setUserReactions] = useState<Set<string>>(new Set())
// Function to format reaction count // Function to format reaction count
const formatCount = (count: number): string => { const formatCount = (count: number): string => {
if (count < 1000) return count.toString() if (count < 1000) return count.toString()
@ -65,34 +62,33 @@ function PostReactions({ post }: PostReactionsProps) {
return `${Math.floor(count / 1000)}K` return `${Math.floor(count / 1000)}K`
} }
// Function to handle reaction click // NOOP handlers for react/unreact functionality
const handleReactionClick = (emoji: string) => { const handleReactionClick = (emoji: string) => {
setUserReactions((prev) => { console.log(`Reaction clicked: ${emoji}`)
const newReactions = new Set(prev) // This would normally call an API to add/remove a reaction
if (newReactions.has(emoji)) {
newReactions.delete(emoji)
} else {
newReactions.add(emoji)
}
return newReactions
})
} }
// Find existing reactions to display
const reactionMap = new Map(post.reactions.map(r => [r.emoji, r]))
return ( return (
<div className="flex flex-wrap gap-2 mt-3 justify-end"> <div className="flex flex-wrap gap-2 mt-3 justify-end">
{post.reactions.map((reaction) => { {post.possibleReactions.map((emoji) => {
const isSelected = userReactions.has(reaction.emoji) const reaction = reactionMap.get(emoji)
const count = reaction?.count || 0
const didReact = reaction?.didReact || false
return ( return (
<button <button
key={reaction.emoji} key={emoji}
onClick={() => handleReactionClick(reaction.emoji)} onClick={() => handleReactionClick(emoji)}
className={`flex items-center px-2 py-1 rounded-full border ${ className={`flex items-center px-2 py-1 rounded-full border ${
isSelected ? 'bg-gray-100 border-gray-400' : 'bg-white border-gray-200' didReact ? 'bg-gray-100 border-gray-400' : 'bg-white border-gray-200'
} hover:bg-gray-100 transition-colors`} } hover:bg-gray-100 transition-colors`}
> >
<span className="mr-1">{reaction.emoji}</span> <span className="mr-1">{emoji}</span>
<span className="text-xs text-gray-600"> <span className="text-xs text-gray-600">
{formatCount(reaction.count + (isSelected ? 1 : 0))} {formatCount(count)}
</span> </span>
</button> </button>
) )

View file

@ -4,6 +4,7 @@ import { components } from '../../api/schema.ts'
export interface EmojiReaction { export interface EmojiReaction {
emoji: string emoji: string
count: number count: number
didReact: boolean
} }
export class Post { export class Post {
@ -13,6 +14,7 @@ export class Post {
public readonly createdAt: Temporal.Instant public readonly createdAt: Temporal.Instant
public readonly authorName: string public readonly authorName: string
public readonly reactions: EmojiReaction[] public readonly reactions: EmojiReaction[]
public readonly possibleReactions: string[]
constructor( constructor(
postId: string, postId: string,
@ -21,50 +23,15 @@ export class Post {
createdAt: string | Temporal.Instant, createdAt: string | Temporal.Instant,
authorName: string, authorName: string,
reactions: EmojiReaction[] = [], reactions: EmojiReaction[] = [],
possibleReactions: string[] = [],
) { ) {
this.postId = postId this.postId = postId
this.content = content this.content = content
this.media = media this.media = media
this.createdAt = Temporal.Instant.from(createdAt) this.createdAt = Temporal.Instant.from(createdAt)
this.authorName = authorName this.authorName = authorName
this.reactions = reactions.length > 0 ? reactions : this.generateRandomReactions() this.reactions = reactions
} this.possibleReactions = possibleReactions
private generateRandomReactions(): EmojiReaction[] {
// List of popular emojis
const emojis = [
'👍',
'❤️',
'😂',
'🎉',
'🔥',
'👏',
'🙏',
'💯',
'🤔',
'😍',
'🥰',
'😮',
'😢',
'😡',
'🤩',
]
// Randomly select 5 unique emojis
const selectedEmojis: string[] = []
while (selectedEmojis.length < 5) {
const randomIndex = Math.floor(Math.random() * emojis.length)
const emoji = emojis[randomIndex]
if (!selectedEmojis.includes(emoji!)) {
selectedEmojis.push(emoji!)
}
}
// Create reaction objects with random counts
return selectedEmojis.map((emoji) => ({
emoji,
count: Math.floor(Math.random() * 50), // Random count between 0 and 49
}))
} }
public static fromDto(dto: components['schemas']['PostDto']): Post { public static fromDto(dto: components['schemas']['PostDto']): Post {
@ -74,6 +41,12 @@ export class Post {
dto.media.map((m) => new PostMediaImpl(new URL(m.url), m.width, m.height)), dto.media.map((m) => new PostMediaImpl(new URL(m.url), m.width, m.height)),
Temporal.Instant.from(dto.createdAt), Temporal.Instant.from(dto.createdAt),
dto.author.username, dto.author.username,
dto.reactions.map((r) => ({
emoji: r.emoji,
count: r.count,
didReact: r.didReact
})),
dto.possibleReactions
) )
} }
} }