docbrown/app/controllers/chat_channels_controller.rb
Molly Struve e87dead7ad
Use new UserSetting and UserNotificationSettings and Ignore Related User Table Fields (#14121)
* schema file undelete description

* feat: v1 of the script

* Flesh out remaining enums under their categories

* complete UsersSettings data update script

* complete DUS for relevant attributes in users and profiles tables

* complete DUS for users_notification_settings

* alphabetize user_settings sql file

* safeguard against null values for "null: false" settings

* Set up actual UsersSettings DUS and specs files

* fix broken DUS script

* complete specs for UsersSetting DUS

* Address QA of specs

* complete specs for users_notification_settings DUS

* fix the typos (thanks Julianna!)

* begin implementation

* still building

* add missing attribute "email_membership_newsletter"

* complete sync code (except race condition for user profile)

* complete implementation, remains tests

* Address PR review and fix Travis fails

* remove superfluous Profile.new

* fix travis fails

* feat: update the users_notification_setting attributes from the user model

* feat: use the config fonts enums to display the fonts

* feat: loop through the keys

* fix profile = nil blowing up; add specs for notification_setting model

* remove unneeded spec

* remove feed validation until after sync code removed; fixes feed_import spec failures

* remove spec associated with feed_url validation in user_setting model

* fix failing spec 😅

* add TODO

* feat: set the user settings in the user controller  and use it in the customization form

* feat: move some update logic to the users settings controller thats being used from customization

* feat: show the updated values form the users_settingd and not the user instance

* Generalize redirect back to current tab

* still trying to reflect changed theme upon refresh

* customizations take effect on refresh

* remove 'with_feed' scope from user model

Co-authored-by: Jamie Gaskins <jamie@forem.com>

* start with takeover for fields previously in profiles table

* Takeover code for `publishing_from_rss` section in Settings (#13914)

* implement takeover code part 1

* implement takeover code

* fix feed fetch

* need rhymes help

* complete implementation; specs pending

* fix STUPID omission that caused so many headaches 😫

* implement profile fields pointing to users_settings 🎉

* run migrations

* implement inbox type & guidelines takeover code; specs pending (#13911)

* Point changes in notification settings to `users_notification_settings` table (#13910)

* implement takeover code; remains specs

* address PR feedback; remove related sync code

* address PR review feedback

* need help with routing and specs

* address pr review

* addressing pr review

* Treat implementation edge cases and omissions 😅

* fix uncommented comment

* fixing implementation cases

* address more PR review feedback

* fixing notifications use-cases

* refactor settings controller

* more pr review changes

* solving bugs

* fix broken onboarding

* handle eperience_level calls

* more fixes

* remove unneeded mappings

* add To-dos for quety updates

* remove done TODO

* purge done TODOs

* update notification_settings-related queries

* start fixing specs

* fixing specs

* fix notification and lrg_forem specs

* fixing broken specs

* still fixing

* fix line dif and remove reloads from user.rb

* run specs

* silence bullet and other fixes

* remove setting migration scripts and specs, fix more settings for specs

* handle missing user for article builder and fix notification specs

* fix some final controller specs and re-add incorrectly removed specs

* remove deprecated data update scripts and related workers, put travis back

* refactor admin tags mods controller, write/move specs for users notifications settings controller

* schema cleanup and other small refactors for consistency

* set field we can invalidate in spec via active record instead of at the db level

* remove I think an uneccessary hook call from subscribe_to_mailchimp_newsletter

* use bnefore_create to setup settings, please dont blow up the test suite

* mailchimp bot fix

* remove decorator in favor of single model method

Co-authored-by: Arit Amana <msarit@gmail.com>
Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com>
Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>
Co-authored-by: Jamie Gaskins <jamie@forem.com>
2021-07-08 09:31:34 -05:00

268 lines
9.4 KiB
Ruby

class ChatChannelsController < ApplicationController
before_action :authenticate_user!, only: %i[moderate]
before_action :set_channel, only: %i[show update update_channel open moderate]
after_action :verify_authorized
include MessagesHelper
include ChatChannelsHelper
CHANNEL_ATTRIBUTES_FOR_SERIALIZATION = %i[id description channel_name].freeze
private_constant :CHANNEL_ATTRIBUTES_FOR_SERIALIZATION
def index
case params[:state]
when "unopened"
authorize ChatChannel
@chat_channels_memberships = unopened_json_response
render "index", formats: :json
when "unopened_ids"
authorize ChatChannel
@unopened_ids = unopened_ids_response
render json: { unopened_ids: @unopened_ids }
when "pending"
authorize ChatChannel
@chat_channels_memberships = pending_json_response
render "index", formats: :json
when "joining_request"
authorize ChatChannel
@chat_channels_memberships = joining_request_json_response
render "index", formats: :json
else
skip_authorization
render_channels_html
end
end
def show
@chat_messages = @chat_channel.messages
.includes(:user)
.order(created_at: :desc)
.offset(params[:message_offset])
.limit(50)
end
def create
authorize ChatChannel
@chat_channel = current_user.chat_channels.create(
channel_type: "invite_only",
channel_name: chat_channel_params[:channel_name],
slug: chat_channel_params[:slug],
)
render_chat_channel
end
def update
chat_channel = ChatChannels::UpdateChannel.call(@chat_channel, chat_channel_params)
if chat_channel.errors.any?
flash[:error] = chat_channel.errors.full_messages.to_sentence
else
if chat_channel_params[:discoverable].to_i.zero?
ChatChannelMembership.create(user_id: Settings::General.mascot_user_id, chat_channel_id: chat_channel.id,
role: "member", status: "active")
else
ChatChannelMembership.find_by(user_id: Settings::General.mascot_user_id)&.destroy
end
flash[:settings_notice] = "Channel settings updated."
end
current_user_membership = @chat_channel.mod_memberships.find_by!(user: current_user)
redirect_to edit_chat_channel_membership_path(current_user_membership)
end
def update_channel
@chat_channel.update(chat_channel_params)
if @chat_channel.errors.any?
render json: { success: false, errors: @chat_channel.errors.full_messages,
message: "Channel settings updation failed. Try again later." }, success: :bad_request
else
if chat_channel_params[:discoverable]
ChatChannelMembership.create(user_id: Settings::General.mascot_user_id, chat_channel_id: @chat_channel.id,
role: "member", status: "active")
else
ChatChannelMembership.find_by(user_id: Settings::General.mascot_user_id)&.destroy
end
render json: { success: true, message: "Channel settings updated.", data: {} }, success: :ok
end
end
def open
membership = @chat_channel.chat_channel_memberships.where(user_id: current_user.id).first
membership.update(last_opened_at: 1.second.from_now, has_unopened_messages: false)
send_open_notification
render json: { status: "success", channel: params[:id] }, status: :ok
end
def moderate
chat_channel = ChatChannel.find_by(id: params[:id])
authorize chat_channel
command, username = chat_channel_params[:command].split
case command
when "/ban"
user = User.find_by(username: username)
membership = user&.chat_channel_memberships&.find_by(chat_channel: chat_channel)
if user && membership
user.add_role(:suspended)
user.messages.where(chat_channel: chat_channel).delete_all
membership.update(status: "removed_from_channel")
Pusher.trigger(chat_channel.pusher_channels, "user-banned", { userId: user.id }.to_json)
render json: { status: "moderation-success", message: "#{username} was suspended.", userId: user.id,
chatChannelId: chat_channel.id }, status: :ok
else
render json: {
status: "error",
message: "Suspend failed. user with username '#{username}' not found in this channel."
}, status: :bad_request
end
when "/unban"
user = User.find_by(username: username)
if user
user.remove_role(:suspended)
render json: { status: "moderation-success", message: "#{username} was unsuspended." }, status: :ok
else
render json: {
status: "error",
message: "Unsuspend failed. User with username '#{username}' not found in this channel."
}, status: :bad_request
end
when "/clearchannel"
@chat_channel.clear_channel
render json: { status: "success", message: "cleared!" }, status: :ok
else
render json: { status: "error", message: "invalid command" }, status: :bad_request
end
end
def create_chat
chat_recipient = User.find(params[:user_id])
valid_listing = Listing.where(user_id: params[:user_id], contact_via_connect: true).limit(1)
authorize ChatChannel
if chat_recipient.setting.inbox_type == "open" || valid_listing.length == 1
chat = ChatChannels::CreateWithUsers.call(users: [current_user, chat_recipient], channel_type: "direct")
message_markdown = params[:message]
message = Message.new(
chat_channel: chat,
message_markdown: message_markdown,
user: current_user,
)
chat.messages.append(message)
render json: { status: "success", message: "chat channel created!" }, status: :ok
else
render json: { status: "error", message: "not allowed!" }, status: :bad_request
end
rescue StandardError => e
render json: { status: "error", message: e.message }, status: :bad_request
end
def block_chat
chat_channel = ChatChannel.find(params[:chat_id])
authorize chat_channel
chat_channel.status = "blocked"
chat_channel.save
render json: { status: "success", message: "chat channel blocked" }, status: :ok
end
# NOTE: this is part of an effort of moving some things from the external to
# the internal API. No behavior was changes as part of this refactoring, so
# this action is a bit unusual.
def channel_info
skip_authorization
@chat_channel =
ChatChannel
.select(CHANNEL_ATTRIBUTES_FOR_SERIALIZATION)
.find_by(id: params[:id])
return if @chat_channel&.has_member?(current_user)
render json: { error: "not found", status: 404 }, status: :not_found
end
def create_channel
chat_channel_params = params[:chat_channel]
chat_channel_name = chat_channel_params[:channel_name].split.join("-")
chat_channel = ChatChannel.new(
channel_type: "invite_only",
channel_name: chat_channel_params[:channel_name],
slug: "#{chat_channel_name}-#{SecureRandom.hex(5)}",
)
authorize chat_channel
chat_channel.save
membership = chat_channel.chat_channel_memberships.new(user_id: current_user.id, role: "mod")
if membership.save
message = ChatChannels::SendInvitation.call(
chat_channel_params[:invitation_usernames],
current_user,
chat_channel,
)
send_chat_action_message(
"channel is created by #{current_user.username}",
current_user, membership.chat_channel_id,
"chat channel is created"
)
render json: {
success: true,
message: "Channel is created #{message ? "& #{message}" : nil}"
}, status: :ok
else
render json: {
success: false,
message: membership.errors_as_sentence
}, status: 445
end
end
private
def set_channel
@chat_channel = ChatChannel.find_by(id: params[:id]) || not_found
authorize @chat_channel
end
def chat_channel_params
params.require(:chat_channel).permit(policy(ChatChannel).permitted_attributes)
end
def render_channels_html
return unless current_user && params[:slug]
slug = if params[:slug]&.start_with?("@")
[current_user.username, params[:slug].delete("@")].sort.join("/")
else
params[:slug]
end
chat_channel = ChatChannel.find_by(slug: slug)
return unless chat_channel
membership = chat_channel.chat_channel_memberships.find_by(user_id: current_user.id)
@active_channel = membership&.status == "active" ? chat_channel : nil
end
def render_chat_channel
if @chat_channel.valid?
render json: { status: "success",
chat_channel: @chat_channel.to_json(only: %i[channel_name slug]) },
status: :ok
else
render json: { errors: @chat_channel.errors.full_messages }
end
end
def send_open_notification
adjusted_slug = if @chat_channel.group?
@chat_channel.adjusted_slug
else
@chat_channel.adjusted_slug(current_user)
end
payload = { channel_type: @chat_channel.channel_type, adjusted_slug: adjusted_slug }.to_json
Pusher.trigger(ChatChannel.pm_notifications_channel(session_current_user_id), "message-opened", payload)
end
def send_chat_action_message(message, user, channel_id, action)
temp_message_id = SecureRandom.hex(20)
message = Message.create(message_markdown: message, user_id: user.id, chat_channel_id: channel_id,
chat_action: action)
pusher_message_created(false, message, temp_message_id)
end
end