❤️ Like/Unlike Posts
Thích (like) hoặc bỏ thích (unlike) bài post.
Core Package (@xhub-chat/core)
Like a Post
import { createClient } from '@xhub-chat/core';
const client = createClient({
baseUrl: 'https://your-server.com',
accessToken: 'your-access-token',
userId: '@user:server.com',
});
const room = client.getRoom(roomId);
if (room) {
// Like a post
await room.likePost(postId);
console.log('Post liked successfully');
}
Unlike a Post
// Unlike a post
await room.unlikePost(postId);
console.log('Post unliked successfully');
Toggle Like/Unlike
async function togglePostLike(room: Room, postId: string, isLiked: boolean) {
try {
if (isLiked) {
await room.unlikePost(postId);
return false; // Now unliked
} else {
await room.likePost(postId);
return true; // Now liked
}
} catch (error) {
console.error('Failed to toggle like:', error);
throw error;
}
}
// Usage
const newLikedState = await togglePostLike(room, postId, currentlyLiked);
Check if User Liked a Post
// Get post details to check like status
const postDetails = await room.getPostDetails(postId);
const userLiked = postDetails.data.is_liked; // Assuming API returns this
const likesCount = postDetails.data.likes_count;
console.log(`Post has ${likesCount} likes`);
console.log(`Current user liked: ${userLiked}`);
React Package (@xhub-chat/react)
Like Button Component
import { useRoom } from '@xhub-chat/react';
import { useState } from 'react';
function LikeButton({
roomId,
postId,
initialLiked = false,
initialCount = 0
}: Props) {
const room = useRoom(roomId);
const [isLiked, setIsLiked] = useState(initialLiked);
const [likesCount, setLikesCount] = useState(initialCount);
const [loading, setLoading] = useState(false);
const handleToggleLike = async () => {
if (!room || loading) return;
setLoading(true);
try {
if (isLiked) {
await room.unlikePost(postId);
setIsLiked(false);
setLikesCount(prev => prev - 1);
} else {
await room.likePost(postId);
setIsLiked(true);
setLikesCount(prev => prev + 1);
}
} catch (error) {
console.error('Failed to toggle like:', error);
} finally {
setLoading(false);
}
};
return (
<button
onClick={handleToggleLike}
disabled={loading}
className={isLiked ? 'liked' : 'not-liked'}
>
{isLiked ? '❤️' : '🤍'} {likesCount}
</button>
);
}
Custom Hook for Post Likes
import { useRoom } from '@xhub-chat/react';
import { useState, useCallback } from 'react';
function usePostLike(roomId: string, postId: string) {
const room = useRoom(roomId);
const [isLiked, setIsLiked] = useState(false);
const [likesCount, setLikesCount] = useState(0);
const [loading, setLoading] = useState(false);
const toggleLike = useCallback(async () => {
if (!room || loading) return;
setLoading(true);
try {
if (isLiked) {
await room.unlikePost(postId);
setIsLiked(false);
setLikesCount(prev => Math.max(0, prev - 1));
} else {
await room.likePost(postId);
setIsLiked(true);
setLikesCount(prev => prev + 1);
}
} catch (error) {
console.error('Failed to toggle like:', error);
throw error;
} finally {
setLoading(false);
}
}, [room, postId, isLiked, loading]);
return {
isLiked,
likesCount,
loading,
toggleLike,
setIsLiked,
setLikesCount,
};
}
// Usage
function PostCard({ roomId, postId }: Props) {
const { isLiked, likesCount, loading, toggleLike } = usePostLike(
roomId,
postId
);
return (
<button onClick={toggleLike} disabled={loading}>
{isLiked ? '❤️' : '🤍'} {likesCount}
</button>
);
}
Post with Like Feature
import { useRoom } from '@xhub-chat/react';
import { useState, useEffect } from 'react';
function PostWithLike({ roomId, postId }: Props) {
const room = useRoom(roomId);
const [post, setPost] = useState<Post | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!room) return;
room.getPostDetails(postId)
.then(response => setPost(response.data))
.catch(console.error)
.finally(() => setLoading(false));
}, [room, postId]);
const handleLike = async () => {
if (!room || !post) return;
try {
if (post.is_liked) {
await room.unlikePost(postId);
} else {
await room.likePost(postId);
}
// Refresh post data
const updated = await room.getPostDetails(postId);
setPost(updated.data);
} catch (error) {
console.error('Failed to toggle like:', error);
}
};
if (loading) return <div>Loading...</div>;
if (!post) return <div>Post not found</div>;
return (
<div className="post">
<p>{post.content}</p>
<button onClick={handleLike}>
{post.is_liked ? '❤️' : '🤍'} {post.likes_count}
</button>
</div>
);
}
Animated Like Button
import { useRoom } from '@xhub-chat/react';
import { useState } from 'react';
import './LikeButton.css'; // Custom animations
function AnimatedLikeButton({ roomId, postId, post }: Props) {
const room = useRoom(roomId);
const [isLiked, setIsLiked] = useState(post.is_liked);
const [likesCount, setLikesCount] = useState(post.likes_count);
const [isAnimating, setIsAnimating] = useState(false);
const handleLike = async () => {
if (!room || isAnimating) return;
setIsAnimating(true);
const wasLiked = isLiked;
// Optimistic update
setIsLiked(!wasLiked);
setLikesCount(prev => wasLiked ? prev - 1 : prev + 1);
try {
if (wasLiked) {
await room.unlikePost(postId);
} else {
await room.likePost(postId);
}
} catch (error) {
// Rollback on error
setIsLiked(wasLiked);
setLikesCount(prev => wasLiked ? prev + 1 : prev - 1);
console.error('Failed to toggle like:', error);
} finally {
setTimeout(() => setIsAnimating(false), 300);
}
};
return (
<button
onClick={handleLike}
className={`like-button ${isLiked ? 'liked' : ''} ${isAnimating ? 'animating' : ''}`}
>
<span className="heart-icon">
{isLiked ? '❤️' : '🤍'}
</span>
<span className="count">{likesCount}</span>
</button>
);
}
API Reference
Room Methods
// Like a post
likePost(postId: string): Promise<HttpResponse<unknown>>
// Unlike a post
unlikePost(postId: string): Promise<HttpResponse<unknown>>
Post Like Properties
interface Post {
id: string;
is_liked: boolean; // Whether current user liked
likes_count: number; // Total likes
// ... other properties
}
Response
// Both likePost and unlikePost return:
Promise<HttpResponse<unknown>>