PageReactions functionality on Astro

Building a Reaction component for astro pages

astroelasticsearchreactionscomponent

Here is a write-up of how I did page reactions on this blog.

The Backend

Server-side layer, keeps track of who reacted when. Also can give us the reaction data for a specific post.

Storage

I went with a cloud elasticsearch provider which allows me do some fast prototyping. You can read some details here.

Backend Layer

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.

GraphQL ERD

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!
}

Frontend

Client-side layer

Component

The component is PageReactions.astro

Frontmatter & Html

---
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> &nbsp; <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>

Javascript

<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>

Vanilla JS

This version uses vanilla javascript, and relies on another component AuthUser.astro which uses Discord for authentication.

Conclusion

end


Reactions:  

Back to Post List