Skip to content

Commit

Permalink
✨ adds feature to LIKE posts
Browse files Browse the repository at this point in the history
  • Loading branch information
LuiseFreese committed Nov 20, 2024
1 parent c7c9e19 commit c31dfbe
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
grid-template-columns: repeat(2, 1fr); /* Two columns */
gap: 20px; /* Consistent gap between cards */
grid-auto-rows: minmax(100px, auto); /* Ensure consistent row height */
grid-auto-flow: dense; /* Fill in gaps with smaller items */
}

.card {
Expand Down Expand Up @@ -47,6 +48,9 @@
.cardContent {
margin-top: 10px;
color: black; /* Set post text color to black */
white-space: pre-wrap; /* Ensure text wraps properly */
word-wrap: break-word; /* Handle long words or links */
overflow-wrap: break-word; /* Handle long words or links */
}

.cardImagesContainer {
Expand Down Expand Up @@ -78,21 +82,23 @@
.statItem {
align-items: center;
display: flex;
cursor: pointer; /* Add cursor pointer for clickable items */
}

.statIcon {
color: #0078d4;
color: #0078d4; /* Blue color */
margin-right: 5px;
font-size: 10px;
font-size: 14px; /* Make icons smaller */
transition: color 0.2s ease; /* Add transition for color change */
}

.statText {
color: #0078d4;
color: #0078d4; /* Blue color */
margin-right: 10px;
}

.hashtag {
color: #0078d4;
color: #0078d4; /* Blue color */
cursor: pointer;
font-weight: 500;
margin-right: 5px;
Expand All @@ -105,4 +111,44 @@
&:visited {
color: #0078d4; /* Ensure the color remains the same for visited links */
}
}

.liked {
color: red; /* Change color to red when liked */
}

.statItem:hover .statIcon {
color: red; /* Change color to red on hover */
}

.linkPreview {
display: flex;
margin-top: 10px;
}

.linkImage {
border-radius: 8px;
height: 100px;
width: 100px;
margin-right: 10px;
}

.linkDetails {
display: flex;
flex-direction: column;
}

.linkTitle {
color: #0078d4; /* Blue color */
font-weight: bold;
text-decoration: none;

&:hover {
text-decoration: underline;
}
}

.linkDescription {
color: #555;
font-size: 0.9rem;
}
129 changes: 53 additions & 76 deletions samples/react-bluesky/src/webparts/blueSky/components/BlueSky.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { Card, CardSection } from '@fluentui/react-cards';
import { initializeIcons } from '@fluentui/react';
import { Icon } from '@fluentui/react/lib/Icon';
import BlueSkyImageSection from './BlueSkyImageSection';
import BlueSkyAuthorSection from './BlueSkyAuthorSection';
import BlueSkyContentSection from './BlueSkyContentSection';
import BlueSkyTimestampSection from './BlueSkyTimestampSection';
import useAccessToken from './useAccessToken';
import useBlueSkyPosts from './useBlueSkyPosts';
import useLikePost from './useLikePost';
import usePostMetrics from './usePostMetrics';
import styles from './BlueSky.module.scss';
import { IBlueSkyProps } from './IBlueSkyProps';
import axios from 'axios';
Expand All @@ -16,61 +17,32 @@ import axios from 'axios';
initializeIcons();

const pdsUrl = "https://bsky.social";
const getPostThreadEndpoint = `${pdsUrl}/xrpc/app.bsky.feed.getPostThread`;

const getPostMetrics = async (accessToken: string, postUri: string): Promise<{ likeCount: number; shareCount: number; replyCount: number }> => {
const headers = { Authorization: `Bearer ${accessToken}` };
const params = { uri: postUri };

try {
const response = await axios.get(getPostThreadEndpoint, { headers, params });
const data = response.data;

console.log('API Response:', data); // Log the full API response

const thread = data.thread || {};
const post = thread.post || {};

console.log('Post Data:', post); // Log the post data

const likeCount = post.likeCount || 0;
const shareCount = post.reshareCount || 0;
const replyCount = post.replyCount || 0;

return { likeCount, shareCount, replyCount };
} catch (error) {
console.error(`Failed to fetch metrics for post ${postUri}`, error);
throw error;
}
};

const BlueSky: React.FC<IBlueSkyProps> = (props) => {
const { accessToken, error: tokenError } = useAccessToken('luisefreese.bsky.social', 'txmo-2fvo-vwb3-3vwa');
const { accessToken, error: tokenError } = useAccessToken('<your handle>.bsky.social', 'your app password'); // replace!
const { posts, loading, error: postsError } = useBlueSkyPosts(accessToken);

const [metrics, setMetrics] = useState<{ [key: string]: { likeCount: number, shareCount: number, replyCount: number } }>({});
const [did, setDid] = useState<string>('');

useEffect(() => {
const fetchMetrics = async (): Promise<void> => {
const fetchDid = async (): Promise<void> => {
if (accessToken) {
const newMetrics: { [key: string]: { likeCount: number, shareCount: number, replyCount: number } } = {};
for (const post of posts) {
const postUri = post.uri.startsWith('at://') ? post.uri : `at://${post.uri}`;
try {
const postMetrics = await getPostMetrics(accessToken, postUri);
newMetrics[post.id] = postMetrics;
} catch (error) {
console.error(`Failed to fetch metrics for post ${post.id}`, error);
}
try {
const response = await axios.get(
`${pdsUrl}/xrpc/com.atproto.identity.resolveHandle?handle=<your handle>.bsky.social`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
setDid(response.data.did);
} catch (error) {
console.error('Failed to fetch DID', error);
}
setMetrics(newMetrics);
}
};

fetchMetrics()
.then(() => console.log("Metrics fetched successfully"))
.catch((error) => console.error("Error fetching metrics:", error));
}, [accessToken, posts]);
fetchDid().catch((error) => console.error('Error fetching DID:', error));
}, [accessToken]);

const { likedPosts, handleLikeClick } = useLikePost(accessToken || '', did || '');
const metrics = usePostMetrics(accessToken || '', posts);

return (
<div>
Expand All @@ -82,41 +54,46 @@ const BlueSky: React.FC<IBlueSkyProps> = (props) => {
{posts.map((post) => {
const lastUriSegment = post.uri.split('/').pop();
const postUrl = `https://bsky.app/profile/${post.author.handle}/post/${lastUriSegment}`;
const profileUrl = `https://bsky.app/profile/${post.author.handle}`;
const postMetrics = metrics[post.id] || { likeCount: 0, shareCount: 0, replyCount: 0 };
const isLiked = likedPosts.has(post.id);

return (
<a key={post.id} href={postUrl} target="_blank" rel="noopener noreferrer" className={styles.cardLink}>
<Card className={styles.card}>
<CardSection>
<BlueSkyAuthorSection avatar={post.author.avatar || ''} author={post.author.displayName} />
</CardSection>
<CardSection>
<Card key={post.id} className={styles.card}>
<CardSection>
<div className={styles.cardAuthorContainer} onClick={() => window.open(profileUrl, '_blank')}>
<img src={post.author.avatar || ''} alt={post.author.displayName} className={styles.cardAvatar} />
<span className={styles.cardAuthor}>{post.author.displayName}</span>
</div>
</CardSection>
<CardSection>
<div onClick={() => window.open(postUrl, '_blank')}>
<BlueSkyContentSection content={post.content} />
</CardSection>
<CardSection>
<BlueSkyImageSection images={post.images || []} did={post.did} />
</CardSection>
<CardSection>
<BlueSkyTimestampSection timestamp={post.timestamp} />
</CardSection>
<CardSection>
<div className={styles.postStats}>
<div className={styles.statItem}>
<Icon iconName="Heart" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.likeCount}</span>
</div>
<div className={styles.statItem}>
<Icon iconName="Share" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.shareCount}</span>
</div>
<div className={styles.statItem}>
<Icon iconName="Comment" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.replyCount}</span>
</div>
</div>
</CardSection>
<CardSection>
<BlueSkyImageSection images={post.images || []} did={post.did} />
</CardSection>
<CardSection>
<BlueSkyTimestampSection timestamp={post.timestamp} />
</CardSection>
<CardSection>
<div className={styles.postStats}>
<div className={`${styles.statItem} ${isLiked ? styles.liked : ''}`} onClick={() => handleLikeClick(post.uri, post.id).catch((error) => console.error('Error liking post:', error))}>
<Icon iconName="Heart" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.likeCount + (isLiked ? 1 : 0)}</span>
</div>
<div className={styles.statItem}>
<Icon iconName="Share" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.shareCount}</span>
</div>
<div className={styles.statItem}>
<Icon iconName="Comment" className={styles.statIcon} />
<span className={styles.statText}>{postMetrics.replyCount}</span>
</div>
</CardSection>
</Card>
</a>
</div>
</CardSection>
</Card>
);
})}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface BlueSkyAuthorSectionProps {
}

const BlueSkyAuthorSection: React.FC<BlueSkyAuthorSectionProps> = ({ avatar, author }) => {
console.log('Author:', author); // Log the author prop to the console


return (
<div className={styles.cardAuthorContainer}>
Expand Down
Loading

0 comments on commit c31dfbe

Please sign in to comment.