Skip to content

Commit 94548f1

Browse files
committed
feat: Add like button UI and mock functionality
1 parent 905ec23 commit 94548f1

2 files changed

Lines changed: 242 additions & 1 deletion

File tree

community/index.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ title: Community Papers
3434
<div class="paper-date" style="display:none;">{{ paper.date | date: '%B %Y' }}</div>
3535
<div>
3636
<a class="talk-title-link" href="{{ paper.url }}">Details <i class="bi bi-box-arrow-up-right"></i></a>
37+
<span class="like-widget" data-paper-id="{{ paper.title | slugify }}">
38+
<button class="like-button"><i class="bi bi-heart"></i></button>
39+
<span class="likes-count">0</span>
40+
</span>
3741
</div>
3842
<details>
3943
<summary>Abstract</summary>
@@ -110,6 +114,86 @@ document.addEventListener('DOMContentLoaded', function() {
110114
// Initial display - show all papers sorted by date
111115
allPaperElements.forEach(p => papersList.appendChild(p));
112116
displayPapers();
117+
118+
// --- Like functionality (same as on homepage) ---
119+
120+
const API_GATEWAY_URL = 'https://your-api-gateway-url.execute-api.us-east-1.amazonaws.com/prod'; // Placeholder
121+
const WEBSOCKET_URL = 'wss://your-websocket-url.execute-api.us-east-1.amazonaws.com/prod'; // Placeholder
122+
123+
function getLikedPapers() {
124+
return JSON.parse(localStorage.getItem('likedPapers') || '{}');
125+
}
126+
127+
function isPaperLiked(paperId) {
128+
return !!getLikedPapers()[paperId];
129+
}
130+
131+
function setPaperLiked(paperId) {
132+
const likedPapers = getLikedPapers();
133+
likedPapers[paperId] = true;
134+
localStorage.setItem('likedPapers', JSON.stringify(likedPapers));
135+
}
136+
137+
async function fetchLikes(paperId) {
138+
// MOCK: Default to 0 when no backend is available.
139+
return 0;
140+
}
141+
142+
async function postLike(paperId) {
143+
// MOCK
144+
const countEl = document.querySelector(`.like-widget[data-paper-id="${paperId}"] .likes-count`);
145+
if (!countEl) return 0;
146+
const currentLikes = parseInt(countEl.textContent, 10);
147+
return currentLikes + 1;
148+
}
149+
150+
function updateLikeButtonUI(button, paperId) {
151+
const icon = button.querySelector('i');
152+
if (isPaperLiked(paperId)) {
153+
button.classList.add('liked');
154+
icon.className = 'bi bi-heart-fill';
155+
} else {
156+
button.classList.remove('liked');
157+
icon.className = 'bi bi-heart';
158+
}
159+
}
160+
161+
document.querySelectorAll('.like-widget').forEach(async (widget) => {
162+
const paperId = widget.dataset.paperId;
163+
const button = widget.querySelector('.like-button');
164+
const countEl = widget.querySelector('.likes-count');
165+
166+
const initialLikes = await fetchLikes(paperId);
167+
countEl.textContent = initialLikes;
168+
169+
updateLikeButtonUI(button, paperId);
170+
171+
button.addEventListener('click', async () => {
172+
if (isPaperLiked(paperId)) {
173+
console.log('Already liked:', paperId);
174+
return;
175+
}
176+
177+
const newLikes = await postLike(paperId);
178+
countEl.textContent = newLikes;
179+
setPaperLiked(paperId);
180+
updateLikeButtonUI(button, paperId);
181+
});
182+
});
183+
184+
// Mock WebSocket for real-time updates
185+
function connectWebSocket() {
186+
setInterval(() => {
187+
const allWidgets = Array.from(document.querySelectorAll('.like-widget:not([style*="display: none"]) .likes-count'));
188+
if (allWidgets.length === 0) return;
189+
190+
const randomCountEl = allWidgets[Math.floor(Math.random() * allWidgets.length)];
191+
const currentLikes = parseInt(randomCountEl.textContent, 10);
192+
randomCountEl.textContent = currentLikes + 1;
193+
}, 5000);
194+
}
195+
196+
connectWebSocket();
113197
});
114198
</script>
115199

@@ -121,6 +205,34 @@ document.addEventListener('DOMContentLoaded', function() {
121205
padding: 10px;
122206
border-radius: 4px;
123207
}
208+
.like-widget {
209+
display: inline-flex;
210+
align-items: center;
211+
margin-left: 1em;
212+
color: #dc3545;
213+
}
214+
.like-button {
215+
border: none;
216+
background: transparent;
217+
cursor: pointer;
218+
color: inherit;
219+
padding: 0 5px 0 0;
220+
font-size: 1.1em;
221+
}
222+
.like-button .bi-heart-fill {
223+
display: none;
224+
}
225+
.like-button.liked .bi-heart {
226+
display: none;
227+
}
228+
.like-button.liked .bi-heart-fill {
229+
display: inline-block;
230+
}
231+
.likes-count {
232+
margin-left: 0.25em;
233+
min-width: 1em;
234+
text-align: left;
235+
}
124236
#paper-search {
125237
width: 100%;
126238
padding: 10px;

index.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ To understand why these discussions are vital, explore these insightful pieces o
4949
</div>
5050
<div>
5151
<a class="talk-title-link" href="{{ paper.url }}">Details <i class="bi bi-box-arrow-up-right"></i></a>
52+
<span class="like-widget" data-paper-id="{{ paper.title | slugify }}">
53+
<button class="like-button"><i class="bi bi-heart"></i></button>
54+
<span class="likes-count">0</span>
55+
</span>
5256
</div>
5357
<details>
5458
<summary>Abstract</summary>
@@ -210,6 +214,106 @@ document.addEventListener('DOMContentLoaded', function() {
210214
}
211215

212216
searchInput.addEventListener('input', filterPapers);
217+
218+
// --- Like functionality ---
219+
220+
const API_GATEWAY_URL = 'https://your-api-gateway-url.execute-api.us-east-1.amazonaws.com/prod'; // Placeholder
221+
const WEBSOCKET_URL = 'wss://your-websocket-url.execute-api.us-east-1.amazonaws.com/prod'; // Placeholder
222+
223+
function getLikedPapers() {
224+
return JSON.parse(localStorage.getItem('likedPapers') || '{}');
225+
}
226+
227+
function isPaperLiked(paperId) {
228+
return !!getLikedPapers()[paperId];
229+
}
230+
231+
function setPaperLiked(paperId) {
232+
const likedPapers = getLikedPapers();
233+
likedPapers[paperId] = true;
234+
localStorage.setItem('likedPapers', JSON.stringify(likedPapers));
235+
}
236+
237+
async function fetchLikes(paperId) {
238+
// MOCK: In a real implementation, this would fetch from the backend.
239+
// const response = await fetch(`${API_GATEWAY_URL}/papers/${paperId}/likes`);
240+
// const data = await response.json();
241+
// return data.likes;
242+
return 0;
243+
}
244+
245+
async function postLike(paperId) {
246+
// MOCK: In a real implementation, this would post to the backend.
247+
// const response = await fetch(`${API_GATEWAY_URL}/papers/${paperId}/like`, { method: 'POST' });
248+
// const data = await response.json();
249+
// return data.likes;
250+
const countEl = document.querySelector(`.like-widget[data-paper-id="${paperId}"] .likes-count`);
251+
const currentLikes = parseInt(countEl.textContent, 10);
252+
return currentLikes + 1;
253+
}
254+
255+
function updateLikeButtonUI(button, paperId) {
256+
const icon = button.querySelector('i');
257+
if (isPaperLiked(paperId)) {
258+
button.classList.add('liked');
259+
icon.className = 'bi bi-heart-fill';
260+
} else {
261+
button.classList.remove('liked');
262+
icon.className = 'bi bi-heart';
263+
}
264+
}
265+
266+
document.querySelectorAll('.like-widget').forEach(async (widget) => {
267+
const paperId = widget.dataset.paperId;
268+
const button = widget.querySelector('.like-button');
269+
const countEl = widget.querySelector('.likes-count');
270+
271+
const initialLikes = await fetchLikes(paperId);
272+
countEl.textContent = initialLikes;
273+
274+
updateLikeButtonUI(button, paperId);
275+
276+
button.addEventListener('click', async () => {
277+
if (isPaperLiked(paperId)) {
278+
console.log('Already liked:', paperId);
279+
return;
280+
}
281+
282+
const newLikes = await postLike(paperId);
283+
countEl.textContent = newLikes;
284+
setPaperLiked(paperId);
285+
updateLikeButtonUI(button, paperId);
286+
});
287+
});
288+
289+
// Mock WebSocket for real-time updates
290+
function connectWebSocket() {
291+
// In a real implementation, you would connect to your WebSocket endpoint:
292+
// const socket = new WebSocket(WEBSOCKET_URL);
293+
// socket.onmessage = function(event) {
294+
// const data = JSON.parse(event.data);
295+
// if (data.paperId && data.likes) {
296+
// const countEl = document.querySelector(`.like-widget[data-paper-id="${data.paperId}"] .likes-count`);
297+
// if (countEl) {
298+
// countEl.textContent = data.likes;
299+
// }
300+
// }
301+
// };
302+
303+
// This is a mock to simulate another user liking a paper every few seconds
304+
setInterval(() => {
305+
const allWidgets = Array.from(document.querySelectorAll('.like-widget'));
306+
if (allWidgets.length === 0) return;
307+
308+
const randomWidget = allWidgets[Math.floor(Math.random() * allWidgets.length)];
309+
const countEl = randomWidget.querySelector('.likes-count');
310+
311+
const currentLikes = parseInt(countEl.textContent, 10);
312+
countEl.textContent = currentLikes + 1;
313+
}, 5000);
314+
}
315+
316+
connectWebSocket();
213317
});
214318
</script>
215319

@@ -221,8 +325,33 @@ document.addEventListener('DOMContentLoaded', function() {
221325
padding: 10px;
222326
border-radius: 4px;
223327
}
328+
.like-widget {
329+
display: inline-flex;
330+
align-items: center;
331+
margin-left: 1em;
332+
color: #dc3545;
333+
}
334+
.like-button {
335+
border: none;
336+
background: transparent;
337+
cursor: pointer;
338+
color: inherit;
339+
padding: 0 5px 0 0;
340+
font-size: 1.1em;
341+
}
342+
.like-button .bi-heart-fill {
343+
display: none;
344+
}
345+
.like-button.liked .bi-heart {
346+
display: none;
347+
}
348+
.like-button.liked .bi-heart-fill {
349+
display: inline-block;
350+
}
224351
.likes-count {
225-
margin-left: 10px;
352+
margin-left: 0.25em;
353+
min-width: 1em;
354+
text-align: left;
226355
}
227356
#paper-search-home {
228357
width: 100%;

0 commit comments

Comments
 (0)