Here is a write-up of how I did page reactions on this blog.
elasticsearch
Server-side layer, keeps track of who reacted when. Also can give us the reaction data for a specific post.
I went with a cloud elasticsearch provider which allows me do some fast prototyping. You can read some details here.
I will be creating an AWS lambda function, that will work as a GraphQL
API, which I will use to query for data, and to store data into.
Here are the entities I have defined for this portion of the API.
type Query { type Mutation {
postLikes( actionLikePost(
appCode: String!, uid: String!,
postId: String!, av: String!,
page: Int, appCode: String!,
itemsPerPage: Int postId: String!
): PostLikePage ): CanResponse
} }
type Paging { type CanResponse {
total: Int! success: Boolean!
page: Int! message: String!
totalPages: Int! status: String
itemsPerPage: Int! warning: String
} }
type PostLike { type PostLikePage {
uid: String! items: [PostLike]
av: String! paging: Paging
postId: String! }
created: String!
}
Client-side layer
The component is PageReactions.astro
---
let appCode = 'oma';
let postId = Astro.props.postId;
let domain_url = import.meta.env.GQL_ENDPOINT;
let gql_query = 'query { postLikes(appCode:"'+appCode+'",postId:"'+postId+'",itemsPerPage:50) { items { uid created av } paging { total } } }';
let gql_mutation = 'mutation { actionLikePost(uid:"#uid#",av:"#av#",appCode:"'+appCode+'",postId:"'+postId+'") { success message } }';
let avatar_url = 'https://cdn.discordapp.com/avatars/#uid#/#avatar#.png';
---
<div class="page-reactions">
Reactions: <span id="count-reactions"></span> <span id="ppl-reactions"></span>
<div id="reaction-no-user" style="display:none;">
Login to add a reaction!
</div>
<div id="reaction-yes-user" style="display:none;">
<div style="display:none;" id="user-letreact"><span class="btn btn-primary" id="action-react">React!</span></div>
<div style="display:none;" id="user-reacted"><small class="text-muted">You already reacted.</small></div>
</div>
</div>
<script type="text/javascript"
define:vars={{ postId,domain_url,gql_query,gql_mutation,avatar_url }}>
var user = getLoggedInUser();
if (user) {
document.getElementById('reaction-yes-user').style.display = '';
} else {
document.getElementById('reaction-no-user').style.display = '';
}
document.addEventListener('DOMContentLoaded', function () {
var alreadyReacted = false;
axios.post(domain_url, {
query: gql_query
})
.then((response) => {
var uids = '';
console.log(response);
response.data.data.postLikes.items.forEach((entry) => {
uids += '<img src="'+avatar_url.replace('#uid#', entry.uid).replace('#avatar#', entry.av) +
'" height="32" style="border-radius:16px;border:1px solid #ccc;margin-left:-4px;" />';
if (user.id === entry.uid) alreadyReacted = true;
});
document.getElementById('ppl-reactions').innerHTML = uids;
document.getElementById('count-reactions').innerHTML =
response.data.data.postLikes.paging.total;
if (alreadyReacted) {
document.getElementById('user-reacted').style.display = '';
} else {
document.getElementById('user-letreact').style.display = '';
}
});
}, false);
function likePost () {
gql_mutation = gql_mutation.replace('#uid#', user.id).replace('#av#', user.avatar);
axios.post(domain_url, {
query: gql_mutation
})
.then((response) => {
document.getElementById('user-letreact').innerText = 'Thanks for your like!';
});
}
document.getElementById('action-react').addEventListener('click', function () {
likePost();
});
</script>
This version uses vanilla javascript, and relies on another component AuthUser.astro which uses Discord for authentication
.
end