From 03ba5ae54a780922a6496d77691d251090e43aeb Mon Sep 17 00:00:00 2001 From: Daniel Nitsikopoulos Date: Sun, 31 Mar 2024 09:03:40 +1100 Subject: [PATCH] Add reactions to posts --- Gemfile | 2 +- app/relations/posts.rb | 2 ++ app/relations/reactions.rb | 15 ++++++++++++ db/migrate/20240330213418_create_reactions.rb | 14 +++++++++++ slices/admin/repos/post_repo.rb | 1 + slices/admin/templates/posts/index.html.slim | 10 ++++++++ slices/main/actions/posts/react.rb | 23 +++++++++++++++++++ slices/main/config/routes.rb | 2 ++ slices/main/repos/post_repo.rb | 2 +- slices/main/repos/reaction_repo.rb | 11 +++++++++ slices/main/templates/posts/show.html.slim | 4 ++-- .../templates/shared/_reactions.html.slim | 3 +++ 12 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 app/relations/reactions.rb create mode 100644 db/migrate/20240330213418_create_reactions.rb create mode 100644 slices/main/actions/posts/react.rb create mode 100644 slices/main/repos/reaction_repo.rb create mode 100644 slices/main/templates/shared/_reactions.html.slim diff --git a/Gemfile b/Gemfile index 7b1afb7..476d36e 100644 --- a/Gemfile +++ b/Gemfile @@ -67,7 +67,7 @@ gem "warning" group :cli, :development do gem "irb" gem "hanami-reloader", "~> 2.1.0.rc" - gem 'rubocop', require: false + gem "rubocop", require: false end group :cli, :development, :test do diff --git a/app/relations/posts.rb b/app/relations/posts.rb index 723201a..ceda518 100644 --- a/app/relations/posts.rb +++ b/app/relations/posts.rb @@ -11,6 +11,8 @@ module Adamantium has_many :post_trips has_many :trips, through: :post_trips + has_many :reactions + has_many :webmentions end end diff --git a/app/relations/reactions.rb b/app/relations/reactions.rb new file mode 100644 index 0000000..106f300 --- /dev/null +++ b/app/relations/reactions.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Adamantium + module Relations + class Reactions < ROM::Relation[:sql] + schema :reactions, infer: true do + associations do + belongs_to :post + end + end + + auto_struct(true) + end + end +end diff --git a/db/migrate/20240330213418_create_reactions.rb b/db/migrate/20240330213418_create_reactions.rb new file mode 100644 index 0000000..3e14fce --- /dev/null +++ b/db/migrate/20240330213418_create_reactions.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +ROM::SQL.migration do + change do + create_table :reactions do + primary_key :id + foreign_key :post_id, :posts, null: false + column :visitor_identifier, :text, null: false + + index :post_id + unique [:post_id, :visitor_identifier] + end + end +end diff --git a/slices/admin/repos/post_repo.rb b/slices/admin/repos/post_repo.rb index 3cf6dd1..414fa89 100644 --- a/slices/admin/repos/post_repo.rb +++ b/slices/admin/repos/post_repo.rb @@ -64,6 +64,7 @@ module Admin def list posts .where(post_type: ["post", "checkin", "code"]) + .combine(:reactions, :webmentions) .order(Sequel.lit("published_at desc")) .to_a end diff --git a/slices/admin/templates/posts/index.html.slim b/slices/admin/templates/posts/index.html.slim index 06a695d..a49ce8d 100644 --- a/slices/admin/templates/posts/index.html.slim +++ b/slices/admin/templates/posts/index.html.slim @@ -9,6 +9,8 @@ div class="max-w-prose mx-auto" x-data="{ activeTab: 0 }" thead th Details th Date + th Mentions + th Reactions th colspan="2" Actions tbody class="{ 'active': activeTab === 0 }" x-show.transition.in.opacity.duration.600="activeTab === 0" - published_posts.each do |post| @@ -20,6 +22,10 @@ div class="max-w-prose mx-auto" x-data="{ activeTab: 0 }" small class="text-gray-400 dark:text-gray-600" = post.slug td = post.published_at&.strftime("%d %b %Y") + td + = post.webmentions.count + td + = post.reactions.count td a href="/admin/posts/#{post.id}" edit td @@ -36,6 +42,10 @@ div class="max-w-prose mx-auto" x-data="{ activeTab: 0 }" small class="text-gray-400 dark:text-gray-600" = post.slug td = post.published_at&.strftime("%d %b %Y") + td + = post.webmentions.count + td + = post.reactions.count td a href="/admin/posts/#{post.id}" edit td diff --git a/slices/main/actions/posts/react.rb b/slices/main/actions/posts/react.rb new file mode 100644 index 0000000..8944dd7 --- /dev/null +++ b/slices/main/actions/posts/react.rb @@ -0,0 +1,23 @@ +require "digest/sha1" + +module Main + module Actions + module Posts + class React < Action + + include Deps["repos.reaction_repo", "repos.post_repo"] + + def handle(req, res) + post = post_repo.fetch!(req.params[:slug]) + + reaction_repo.create(post_id: post.id, visitor_identifier: Digest::SHA1.hexdigest(req.ip)) + + reaction_count = reaction_repo.count(post_id: post.id) + + res.body = "👍 #{reaction_count}" + res.status = 201 + end + end + end + end +end diff --git a/slices/main/config/routes.rb b/slices/main/config/routes.rb index 40df02e..6309d7d 100644 --- a/slices/main/config/routes.rb +++ b/slices/main/config/routes.rb @@ -50,6 +50,8 @@ module Main get "/timemachine/:year/:month/:day", to: "timemachine.show" + post "/posts/:slug/react", to: "posts.react" + redirect "deploying-a-hanami-app-to-fly-io", to: "/post/deploying-a-hanami-20-app-to-flyio" redirect "deploying-a-hanami-app-to-fly-io/", to: "/post/deploying-a-hanami-20-app-to-flyio" end diff --git a/slices/main/repos/post_repo.rb b/slices/main/repos/post_repo.rb index c59fc74..4263313 100644 --- a/slices/main/repos/post_repo.rb +++ b/slices/main/repos/post_repo.rb @@ -184,7 +184,7 @@ module Main def fetch!(slug) posts .published - .combine(:tags, :trips, :webmentions) + .combine(:tags, :trips, :webmentions, :reactions) .node(:webmentions) { |webmention| webmention.published.where(type: "reply") } diff --git a/slices/main/repos/reaction_repo.rb b/slices/main/repos/reaction_repo.rb new file mode 100644 index 0000000..e4eba5e --- /dev/null +++ b/slices/main/repos/reaction_repo.rb @@ -0,0 +1,11 @@ +module Main + module Repos + class ReactionRepo < Adamantium::Repo[:reactions] + commands :create + + def count(post_id:) + reactions.where(post_id: post_id).count + end + end + end +end diff --git a/slices/main/templates/posts/show.html.slim b/slices/main/templates/posts/show.html.slim index 8193662..1af97c4 100644 --- a/slices/main/templates/posts/show.html.slim +++ b/slices/main/templates/posts/show.html.slim @@ -57,8 +57,8 @@ article class="h-entry" - if post.location img loading="lazy" class="shadow-solid shadow-pink-100 dark:shadow-pink-200 rounded mb-4" src=post.large_map - - if post.webmentions && post.webmentions.count == 0 && post.commentable - a href="mailto:blog@dnitza.com?subject=About that post of yours&body=%0A%0A---%0A(In reply to #{post.permalink})" Reply + div class="max-w-prose mx-auto text-gray-600 dark:text-gray-200 flex" + = render "shared/reactions", post: post - if post.webmentions && post.webmentions.count > 0 div class="mt-12" h3 diff --git a/slices/main/templates/shared/_reactions.html.slim b/slices/main/templates/shared/_reactions.html.slim new file mode 100644 index 0000000..e10fa62 --- /dev/null +++ b/slices/main/templates/shared/_reactions.html.slim @@ -0,0 +1,3 @@ +button hx-post="/posts/#{post.slug}/react" hx-trigger="click" class="px-2 py-1 border rounded border-indigo-900 hover:border-indigo-400 text-indigo-200 mr-2" 👍 #{post.reactions.count} +- if post.webmentions && post.webmentions.count == 0 && post.commentable + a class="no-underline px-2 py-1 border rounded border-indigo-900 hover:border-indigo-400 text-indigo-200 mr-2" href="mailto:blog@dnitza.com?subject=About that post of yours&body=%0A%0A---%0A(In reply to #{post.permalink})" ✉️ Reply