Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add rich text reaction model #451

Merged
merged 38 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8b589c4
feat: add RichTextReaction model
wwrk22 Dec 7, 2023
12c40b3
feat: rollback and remigrate RichTextReaction creation with emoji and…
wwrk22 Dec 8, 2023
dd54eef
chore: add missing association
wwrk22 Dec 8, 2023
869b1e6
feat: allow RichTextReaction to interface with a limited set of emoji…
wwrk22 Dec 8, 2023
ca3a2f9
feat: add routes and actions for rich text reaction create and destroy
wwrk22 Dec 9, 2023
e34fa78
refactor: Emoji class isn't needed
wwrk22 Dec 10, 2023
feed2fa
feat: add rich text reaction view component
wwrk22 Dec 11, 2023
8b8e4a0
feat: have standup update rendered with emoji selector
wwrk22 Dec 11, 2023
dbd7f68
chore: comment for future improvement
wwrk22 Dec 11, 2023
285c7d6
refacto: improve test
wwrk22 Dec 11, 2023
e90e62b
refactor: organize in order to more easily add new rich text reaction…
wwrk22 Dec 12, 2023
a8b5330
rubocop corrections
wwrk22 Dec 12, 2023
d03a799
refactor: using the emoji text as the key is easier to use with bette…
wwrk22 Dec 12, 2023
1f1523b
chore: remove temporary components that were used for testing
wwrk22 Dec 19, 2023
135b632
feat: gather reaction info for all rich text of a class
wwrk22 Dec 19, 2023
c1b5c04
chore: EmojiSelectorComponent has been deleted
wwrk22 Dec 19, 2023
f97b927
refactor: use a more fitting column name
wwrk22 Dec 19, 2023
c21e34e
clean up
wwrk22 Dec 20, 2023
99ae7bc
rubocop corrections
wwrk22 Dec 20, 2023
b43cbb9
refactor: store emojis and their captions for global usage
wwrk22 Dec 22, 2023
62dbb9b
refactor: `Reactionable` is not a fitting way of DRYing up as of now
wwrk22 Dec 26, 2023
c5f9186
chore: use `require` for the one lib class
wwrk22 Dec 26, 2023
5063d0d
refactor: emojis and their captions defnitions were moved
wwrk22 Dec 26, 2023
7348de7
refactor: change Emoji to a reusable class that provides API for fron…
wwrk22 Dec 26, 2023
fb1e1d3
fix: rescuing the error doesn't stop ctlr action flow
wwrk22 Dec 26, 2023
b958d53
chore: clean up tests
wwrk22 Dec 26, 2023
290d63b
chore: rubocop corrections
wwrk22 Dec 26, 2023
e413d32
chore: add helpful comment
wwrk22 Dec 28, 2023
a094daa
feat: update Emoji for easier API for frontend to use
wwrk22 Dec 31, 2023
9e38c27
feat: scope for finding all reactions belonging to given rich text(s)
wwrk22 Dec 31, 2023
f692cc3
rubocop
wwrk22 Dec 31, 2023
dbaf208
refactor: eliminate view component query and do some clean up
wwrk22 Jan 2, 2024
f11a8d0
clean up
wwrk22 Jan 4, 2024
6c34708
chore: revert changes that should go in another branch
wwrk22 Jan 4, 2024
1388b6d
chore: remove scopes that are too trivial and aren't even needed yet
wwrk22 Jan 4, 2024
042a8e8
feat: add factory for ActionText::RichText, skip lint
wwrk22 Jan 8, 2024
56156d4
Merge branch 'main' into feature/add-rich-text-reaction-model
wwrk22 Jan 9, 2024
008127f
chore: add test for code coverage
wwrk22 Jan 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions app/controllers/rich_text_reactions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class RichTextReactionsController < ApplicationController
before_action :build_rich_text_reaction, only: %i[create]

# POST /rich_text_reactions.turbo_stream
def create
if @rich_text_reaction.save
render :create, status: :created
else
flash.now[:alert] = 'Reaction could not be created.'
end
end

# DELETE /rich_text_reactions/:id.turbo_stream
def destroy
@rich_text_reaction = RichTextReaction.find(params[:id])

msg = 'Reaction could not be deleted.'
flash.now[:alert] = msg unless @rich_text_reaction.destroy
rescue ActiveRecord::RecordNotFound
flash.now[:alert] = "RichTextReaction with id #{params[:id]} could not be found."
end

private

def build_rich_text_reaction
@rich_text_reaction = current_user.rich_text_reactions.build(
rich_text_reaction_params
)
end

def rich_text_reaction_params
params.require(:rich_text_reaction).permit(:emoji_caption, :rich_text_id)
end
end
10 changes: 7 additions & 3 deletions app/controllers/standup_meetings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ def index
@standup_meeting_group = StandupMeetingGroup.includes(:users)
.find(params[:standup_meeting_group_id])
authorize @standup_meeting_group, policy_class: StandupMeetingPolicy
@standup_meetings = @standup_meeting_group.standup_meetings
.includes(:user)
.where(meeting_date: @meeting_date)
@standup_meetings = @standup_meeting_group
.standup_meetings
.includes(:user,
:rich_text_yesterday_work_description,
:rich_text_today_work_description,
:rich_text_blockers_description)
.where(meeting_date: @meeting_date)
@current_user_standup_meeting = @standup_meetings.detect do |meeting|
meeting.user == current_user
end || @standup_meeting_group.standup_meetings.new(
Expand Down
33 changes: 33 additions & 0 deletions app/models/rich_text_reaction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# == Schema Information
#
# Table name: rich_text_reactions
#
# id :bigint not null, primary key
# emoji_caption :string not null
# created_at :datetime not null
# updated_at :datetime not null
# rich_text_id :bigint not null
# user_id :bigint not null
#
# Indexes
#
# index_rich_text_reactions_on_rich_text_id (rich_text_id)
# index_rich_text_reactions_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (rich_text_id => action_text_rich_texts.id)
# fk_rails_... (user_id => users.id)
#
require 'emoji'

class RichTextReaction < ApplicationRecord
belongs_to :user
belongs_to :rich_text, class_name: 'ActionText::RichText'

validates :emoji_caption,
inclusion: {
in: Emoji.captions,
message: 'must be present in permissible set'
}
end
2 changes: 2 additions & 0 deletions app/models/standup_meeting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class StandupMeeting < ApplicationRecord
belongs_to :standup_meeting_group, inverse_of: :standup_meetings
belongs_to :user

# Under the hood, Rails calls a `has_one :rich_text_yesterday_work_description`,
# so we can do `StandupMeeting.includes(:rich_text_yesterday_work_description).
has_rich_text :yesterday_work_description
has_rich_text :today_work_description
has_rich_text :blockers_description
Expand Down
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class User < ApplicationRecord

has_many :mentee_applications, class_name: 'UserMenteeApplication', dependent: :destroy

has_many :rich_text_reactions, dependent: :destroy

# rubocop:disable Rails/InverseOf
has_one :current_resume, -> { where(current: true) }, class_name: 'Resume', dependent: nil
# rubocop:enable Rails/InverseOf
Expand Down
1 change: 1 addition & 0 deletions app/views/rich_text_reactions/create.turbo_stream.erb
wwrk22 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= "PLACEHOLDER" %>
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@
scope controller: :static do
get :faq
end

resources :rich_text_reactions, only: %i[create destroy]
end
11 changes: 11 additions & 0 deletions db/migrate/20231207143236_create_rich_text_reactions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateRichTextReactions < ActiveRecord::Migration[7.0]
def change
create_table :rich_text_reactions do |t|
t.references :user, null: false, foreign_key: true
t.references :rich_text, null: false, foreign_key: { to_table: :action_text_rich_texts }
t.string :emoji_caption, null: false

t.timestamps
end
end
end
14 changes: 13 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified erd.pdf
Binary file not shown.
32 changes: 32 additions & 0 deletions lib/emoji.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

class Emoji
DICTIONARY = {
'THUMBS_UP' => '👍',
'THUMBS_DOWN' => '👎',
'THINKING' => '🤔',
'HOORAY' => '🎉',
'SHRUG' => '🤷',
'EYES' => '👀'
}.freeze

attr_reader :caption

def initialize(caption = DICTIONARY.keys.sample)
@caption = caption
end

def emoji
DICTIONARY[@caption]
end

class << self
def captions
DICTIONARY.keys
end

def emojis
DICTIONARY.values

Check warning on line 29 in lib/emoji.rb

View check run for this annotation

Codecov / codecov/patch

lib/emoji.rb#L29

Added line #L29 was not covered by tests
end
end
end
28 changes: 28 additions & 0 deletions spec/factories/rich_text_reactions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# == Schema Information
#
# Table name: rich_text_reactions
#
# id :bigint not null, primary key
# emoji_caption :string not null
# created_at :datetime not null
# updated_at :datetime not null
# rich_text_id :bigint not null
# user_id :bigint not null
#
# Indexes
#
# index_rich_text_reactions_on_rich_text_id (rich_text_id)
# index_rich_text_reactions_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (rich_text_id => action_text_rich_texts.id)
# fk_rails_... (user_id => users.id)
#
FactoryBot.define do
factory :rich_text_reaction do
user
rich_text { create(:standup_meeting).yesterday_work_description }
wwrk22 marked this conversation as resolved.
Show resolved Hide resolved
emoji_caption { Emoji.captions.sample }
end
end
14 changes: 14 additions & 0 deletions spec/lib/emoji_spec.rb
wwrk22 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'rails_helper'
require 'emoji'

RSpec.describe Emoji do
let(:caption) { described_class.captions.sample }

describe '#emoji' do
subject { described_class.new(caption) }

it 'returns the emoji in string format' do
expect(subject.emoji).to eq(Emoji::DICTIONARY[caption])
end
end
end
17 changes: 17 additions & 0 deletions spec/models/rich_text_reactions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'rails_helper'

RSpec.describe RichTextReaction do
it 'is valid with an emoji in the permissible set' do
emoji_caption = Emoji.captions.sample
rich_text_reaction = described_class.new(emoji_caption:)

expect(rich_text_reaction.errors[:emoji_caption]).to be_empty
end

it 'is invalid with an emoji not in the permissible set' do
rich_text_reaction = described_class.new(emoji_caption: 'disguised_face')
rich_text_reaction.valid?

expect(rich_text_reaction.errors[:emoji_caption]).to be_present
end
end
67 changes: 67 additions & 0 deletions spec/requests/rich_text_reactions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'rails_helper'
require 'emoji'

RSpec.describe 'RichTextReaction requests' do
let!(:user) { create(:user) }

before do
sign_in user
end

describe 'POST /rich_text_reactions' do
it 'creates a rich text reaction' do
count = RichTextReaction.count
params = {
rich_text_reaction: {
emoji_caption: Emoji.captions.sample,
rich_text_id: create(:standup_meeting).yesterday_work_description.id
},
format: :turbo_stream
}

post(rich_text_reactions_url, params:)

expect(RichTextReaction.count).to eq(count + 1)
expect(response).to have_http_status :created
end

it 'sets a flash message to indicate failure to create' do
post rich_text_reactions_url,
params: {
rich_text_reaction: {
emoji_caption: Emoji.captions.sample,
rich_text_id: 2
},
format: :turbo_stream
}

expect(flash.now[:alert]).to be_present
end
end

describe 'DELETE /rich_text_reactions/:id' do
it 'deletes a rich text reaction' do
id = create(:rich_text_reaction).id

delete(rich_text_reaction_url(id), params: { format: :turbo_stream })

expect(RichTextReaction.count).to be_zero
expect(response).to have_http_status :no_content
end

it 'sets a flash message to indicate failure to delete' do
fake_id = '1'
rich_text_reaction = instance_double(RichTextReaction, destroy: false)
allow(RichTextReaction).to receive(:find).with(fake_id).and_return(rich_text_reaction)

delete rich_text_reaction_url(fake_id),
params: {
format: :turbo_stream
}

expect(flash.now[:alert]).to be_present

expect(response).to have_http_status :no_content
end
end
end