@@ -165,6 +165,19 @@ en:
message:
wrote: wrote
you: You
notifications:
empty_state:
body: You have no unread notifications.
title: No new notifications
index:
cta: View history
title: New notifications
new_business: "%{business} added their business."
new_conversation: "%{name} (%{company}) started a conversation with %{developer}."
new_developer_profile: "%{developer} added their developer profile."
new_message: "%{sender} sent you a message."
show:
notice: Notification marked as read.
open_graph_tags_component:
default_description: Find Ruby on Rails developers looking for freelance, contract, and full-time work.
preferred_compensation_component:
@@ -201,6 +214,12 @@ en:
pundit:
errors:
unauthorized: You don't have access to that.
read_notifications:
empty_state:
body: You have no read notifications.
title: No read notifications
index:
title: Read notifications
role_type_component:
interested_in_roles: Interested in roles
roles:
@@ -226,8 +245,10 @@ en:
my_business_profile: My business profile
my_conversations: My conversations
my_developer_profile: My developer profile
new_notifications: You have a new notification
open_user_menu: Open user menu
sign_out: Sign out
view_notifications: View notifications
signed_out_component:
register: Register
sign_in: Sign in
@@ -9,6 +9,8 @@
resource :pricing, only: :show, controller: :pricing
resource :role, only: :new
resources :businesses, except: :destroy
resources :read_notifications, only: :index, path: "/notifications/read"
resources :notifications, only: %i[index show]
resources :conversations, only: %i[index show] do
resources :messages, only: :create
resource :block, only: %i[new create]
@@ -7,7 +7,10 @@ def create_user!(name)
)
end

Developer.create!(
admin = create_user!("admin")
admin.update!(admin: true)

developer = Developer.create!(
user: create_user!("Dennis"),
name: "Dennis Ritchie",
available_on: Date.new(2021, 1, 1),
@@ -49,3 +52,6 @@ def create_user!(name)

business.user.set_payment_processor(:fake_processor, allow_fake: true)
business.user.payment_processor.subscribe(plan: "railsdevs")

conversation = Conversation.create!(developer: developer, business: business)
Message.create!(conversation: conversation, sender: business, body: "Let's work together, Dennis!")
@@ -0,0 +1,21 @@
namespace :message_notifications do
task backfill_conversations: :environment do
Notification.where(type: "NewMessageNotification").find_each do |notification|
instance = notification.to_notification
notification.update!(
recipient: notification.recipient.user,
params: {
message: instance.message,
conversation: instance.message.conversation
}
)

# The associated developers with these notifications where deleted.
Notification.where(id: [1, 98]).destroy_all
end
end

task mark_as_read: :environment do
Notification.mark_as_read!
end
end
@@ -22,7 +22,7 @@ class AvailabilityComponentTest < ViewComponent::TestCase
end

test "will show Available in with time to availability" do
@developer.available_on = Date.today + 2.months
@developer.available_on = Date.today + 61.days
render_inline(AvailabilityComponent.new(developer: @developer))

assert_no_selector("svg")
@@ -70,6 +70,54 @@ class UserMenu::SignedInComponentTest < ViewComponent::TestCase
refute_link_to stripe_portal_path
end

test "links to new notifications view when no new notifications exist" do
user = users(:with_business)

render_inline UserMenu::SignedInComponent.new(user)
assert_link_to notifications_path
end

test "links to new notifications view when new notifications exist" do
user = users(:with_business)
developer = developers(:available)
Message.create!(developer: developer, business: user.business, sender: developer, body: "Hello!")

render_inline UserMenu::SignedInComponent.new(user)
assert_link_to notifications_path
end

test "shows red alert dot when new notifications exist" do
user = users(:with_business)
developer = developers(:available)
Message.create!(developer: developer, business: user.business, sender: developer, body: "Hello!")

render_inline UserMenu::SignedInComponent.new(user)
assert_css "bg-red-400"
end

test "does not show red alert dot when no new notifications exist" do
user = users(:with_business)

render_inline UserMenu::SignedInComponent.new(user)
assert_no_css "bg-red-400"
end

def assert_css(class_name)
assert_selector ".#{class_name}"
end

def assert_no_css(class_name)
assert_no_selector ".#{class_name}"
end

def assert_element(id)
assert_selector "##{id}"
end

def assert_no_element(id)
assert_no_selector "##{id}"
end

def assert_link_to(path)
assert_selector "a[href='#{path}']"
end
@@ -47,4 +47,18 @@ class ConversationsTest < ActionDispatch::IntegrationTest

assert_redirected_to root_path
end

test "unread notifictions are marked as read" do
user = users(:with_developer_conversation)
developer = user.developer
business = businesses(:with_conversation)
conversation = conversations(:one)
Message.create!(developer: developer, business: business, body: "Hi!", sender: business, conversation: conversation)
refute Notification.last.read?

sign_in user
get conversation_path(conversation)

assert Notification.last.read?
end
end
@@ -0,0 +1,54 @@
require "test_helper"

class NotificationsTest < ActionDispatch::IntegrationTest
test "you must be signed in" do
get notifications_path
assert_redirected_to new_user_registration_path
end

test "you can view the new notifications page even if none exist" do
sign_in users(:with_business)

get notifications_path
assert_select "h3", "No new notifications"
end

test "you can view your new notifications if you have new (unread) notifications" do
user = users(:with_business)
developer = developers(:available)
sign_in user
create_message!(developer: developer, business: user.business)

get notifications_path
assert_select "h1", "New notifications"
end

test "viewing a notification marks it as read and redirects" do
user = users(:with_business)
developer = developers(:available)
sign_in user
message = create_message!(developer: developer, business: user.business)
notification = Notification.last

refute notification.reload.read?
get notification_path(notification)

assert_redirected_to conversation_path(message.conversation)
assert notification.reload.read?
end

test "redirects to your notifications if the notification doesn't have a URL" do
user = users(:admin)
sign_in user
Conversation.create!(developer: developers(:available), business: businesses(:one))
notification = Notification.last

get notification_path(notification)

assert_redirected_to notifications_path
end

def create_message!(developer:, business:)
Message.create!(developer: developer, business: business, sender: developer, body: "Hello!")
end
end
@@ -0,0 +1,27 @@
require "test_helper"

class ReadNotificationsTest < ActionDispatch::IntegrationTest
test "you must be signed in" do
get read_notifications_path
assert_redirected_to new_user_registration_path
end

test "you can view the history page even if no read notifications exist" do
sign_in users(:with_business)

get read_notifications_path
assert_select "h3", "No read notifications"
end

test "you can view your past notifications if you have past (read) notifications" do
user = users(:with_business)
developer = developers(:available)
sign_in user
Message.create!(developer: developer, business: user.business, sender: developer, body: "Hello!")
Notification.last.mark_as_read!

get read_notifications_path

assert_select "h1", "Read notifications"
end
end
@@ -1,13 +1,16 @@
class AdminMailerPreview < ActionMailer::Preview
def new_developer_profile
AdminMailer.with(developer: Developer.first, recipient: User.first).new_developer_profile
notification = Notification.where(type: NewDeveloperProfileNotification.to_s).first
AdminMailer.with(record: notification, recipient: User.first).new_developer_profile
end

def new_business
AdminMailer.with(business: Business.first, recipient: User.first).new_business
notification = Notification.where(type: NewBusinessNotification.to_s).first
AdminMailer.with(record: notification, recipient: User.first).new_business
end

def new_conversation
AdminMailer.with(conversation: Conversation.first, recipient: User.first).new_conversation
notification = Notification.where(type: NewConversationNotification.to_s).first
AdminMailer.with(record: notification, recipient: User.first).new_conversation
end
end
@@ -1,5 +1,6 @@
class MessageMailerPreview < ActionMailer::Preview
def new_message
MessageMailer.with(message: Message.first).new_message
notification = Notification.where(type: NewMessageNotification.to_s).first
MessageMailer.with(record: notification, recipient: notification.recipient).new_message
end
end
@@ -26,7 +26,10 @@ class MessageTest < ActiveSupport::TestCase
Message.create!(developer: developer, business: business, sender: business, body: "Hello!")
end

assert_equal Notification.last.type, NewMessageNotification.name
assert_equal Notification.last.recipient, developer
notification = Notification.last
assert_equal notification.type, NewMessageNotification.name
assert_equal notification.recipient, developer.user
assert_equal notification.to_notification.message, Message.last
assert_equal notification.to_notification.conversation, Message.last.conversation
end
end
@@ -0,0 +1,19 @@
require "test_helper"

class NotificationTest < ActiveSupport::TestCase
test "conversation resolves correctly" do
developer = developers(:available)
business = businesses(:one)
message = Message.create!(developer: developer, business: business, sender: developer, body: "Hello!")

assert Notification.last.to_notification.conversation == message.conversation
end

test "message resolves correctly" do
developer = developers(:available)
business = businesses(:one)
message = Message.create!(developer: developer, business: business, sender: developer, body: "Hello!")

assert Notification.last.to_notification.message == message
end
end