diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 051f4146..19339bcf 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -51,22 +51,6 @@ module Admin redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end - def enable - authorize @custom_emoji, :enable? - @custom_emoji.update!(disabled: false) - log_action :enable, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg') - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - - def disable - authorize @custom_emoji, :disable? - @custom_emoji.update!(disabled: true) - log_action :disable, @custom_emoji - flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg') - redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) - end - private def set_custom_emoji diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index b65ba69f..d7629be4 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -3,12 +3,15 @@ class Api::BaseController < ApplicationController DEFAULT_STATUSES_LIMIT = 20 DEFAULT_ACCOUNTS_LIMIT = 20 + DEFAULT_CHAT_CONVERSATION_LIMIT = 12 + DEFAULT_CHAT_CONVERSATION_MESSAGE_LIMIT = 10 include RateLimitHeaders skip_before_action :store_current_location skip_before_action :check_user_permissions + before_action :block_if_doorkeeper before_action :set_cache_headers protect_from_forgery with: :null_session @@ -90,6 +93,14 @@ class Api::BaseController < ApplicationController doorkeeper_authorize!(*scopes) if doorkeeper_token end + def superapp? + doorkeeper_token && doorkeeper_token.application.superapp? || false + end + + def block_if_doorkeeper + raise GabSocial::NotPermittedError unless superapp? + end + def set_cache_headers response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' end diff --git a/app/controllers/api/v1/accounts/search_controller.rb b/app/controllers/api/v1/accounts/search_controller.rb new file mode 100644 index 00000000..5cc835c5 --- /dev/null +++ b/app/controllers/api/v1/accounts/search_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::SearchController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:accounts' } + before_action :require_user! + + def show + @accounts = account_search + render json: @accounts, each_serializer: REST::AccountSerializer + end + + private + + def account_search + AccountSearchService.new.call( + params[:q], + current_account, + limit: limit_param(DEFAULT_ACCOUNTS_LIMIT), + resolve: truthy_param?(:resolve), + following: truthy_param?(:following), + offset: params[:offset] + ) + end +end \ No newline at end of file diff --git a/app/controllers/api/v1/chat_conversation_accounts/blocked_accounts_controller.rb b/app/controllers/api/v1/chat_conversation_accounts/blocked_accounts_controller.rb new file mode 100644 index 00000000..3e5bf768 --- /dev/null +++ b/app/controllers/api/v1/chat_conversation_accounts/blocked_accounts_controller.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversationAccounts::BlockedAccountsController < Api::BaseController + before_action -> { doorkeeper_authorize! :follow, :'read:blocks' } + before_action :require_user! + after_action :insert_pagination_headers + + def index + @accounts = load_accounts + render json: @accounts, each_serializer: REST::AccountSerializer + end + + private + + def load_accounts + paginated_blocks.map(&:target_account) + end + + def paginated_blocks + @paginated_blocks ||= ChatBlock.eager_load(target_account: :account_stat) + .where(account: current_account) + .paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def next_path + if records_continue? + api_v1_chat_conversation_accounts_blocked_accounts_url pagination_params(max_id: pagination_max_id) + end + end + + def prev_path + unless paginated_blocks.empty? + api_v1_chat_conversation_accounts_blocked_accounts_url pagination_params(since_id: pagination_since_id) + end + end + + def pagination_max_id + paginated_blocks.last.id + end + + def pagination_since_id + paginated_blocks.first.id + end + + def records_continue? + paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end +end diff --git a/app/controllers/api/v1/chat_conversation_accounts/muted_accounts_controller.rb b/app/controllers/api/v1/chat_conversation_accounts/muted_accounts_controller.rb new file mode 100644 index 00000000..f0f6b6c1 --- /dev/null +++ b/app/controllers/api/v1/chat_conversation_accounts/muted_accounts_controller.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversationAccounts::MutedAccountsController < Api::BaseController + before_action -> { doorkeeper_authorize! :follow, :'read:mutes' } + before_action :require_user! + after_action :insert_pagination_headers + + def index + @accounts = load_accounts + render json: @accounts, each_serializer: REST::AccountSerializer + end + + private + + def load_accounts + paginated_mutes.map(&:target_account) + end + + def paginated_mutes + @paginated_mutes ||= ChatMute.eager_load(target_account: :account_stat) + .where(account: current_account) + .paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def next_path + if records_continue? + api_v1_chat_conversation_accounts_muted_accounts_url pagination_params(max_id: pagination_max_id) + end + end + + def prev_path + unless paginated_mutes.empty? + api_v1_chat_conversation_accounts_muted_accounts_url pagination_params(since_id: pagination_since_id) + end + end + + def pagination_max_id + paginated_mutes.last.id + end + + def pagination_since_id + paginated_mutes.first.id + end + + def records_continue? + paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end +end diff --git a/app/controllers/api/v1/messages_controller.rb b/app/controllers/api/v1/chat_conversation_accounts_controller.rb similarity index 50% rename from app/controllers/api/v1/messages_controller.rb rename to app/controllers/api/v1/chat_conversation_accounts_controller.rb index 207a991d..11ef8459 100644 --- a/app/controllers/api/v1/messages_controller.rb +++ b/app/controllers/api/v1/chat_conversation_accounts_controller.rb @@ -1,24 +1,16 @@ # frozen_string_literal: true -class Api::V1::AccountsController < Api::BaseController - before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow] - before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock] - before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create] +class Api::V1::ChatConversationAccountsController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' }, except: [:create, :follow, :unfollow, :block, :unblock, :mute, :unmute] + before_action -> { doorkeeper_authorize! :write, :'write:chats' }, only: [:create] - before_action :require_user!, except: [:show, :create] + before_action :require_user! before_action :set_account, except: [:create] - before_action :check_account_suspension, only: [:show] def show # end - def create - # - end - def block BlockMessengerService.new.call(current_user.account, @account) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships @@ -42,18 +34,19 @@ class Api::V1::AccountsController < Api::BaseController private def set_account - @account = Account.find(params[:id]) + # @account = Account.find(params[:id]) end - def relationships(**options) - AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options) - end + # def relationships(**options) + # AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options) + # end def check_account_suspension gone if @account.suspended? end - def account_params - params.permit(:username, :email, :password, :agreement, :locale) - end + # def account_params + # params.permit(:username, :email, :password, :agreement, :locale) + # end + end diff --git a/app/controllers/api/v1/chat_conversation_controller.rb b/app/controllers/api/v1/chat_conversation_controller.rb new file mode 100644 index 00000000..31f7feec --- /dev/null +++ b/app/controllers/api/v1/chat_conversation_controller.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversationController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' } + before_action -> { doorkeeper_authorize! :write, :'write:chats' } + + before_action :require_user! + before_action :set_account, only: :create + before_action :set_chat_conversation, only: [:show, :mark_chat_conversation_approved, :mark_chat_conversation_hidden, :mark_chat_conversation_unread] + + def show + puts "tilly ChatConversationsController-0" + render json: {}, each_serializer: REST::ChatConversationAccountSerializer + end + + def create + puts "tilly ChatConversationsController-1" + # : todo : + # check if already created + # check if blocked + # check if chat blocked + # check if allow anyone to message then create with approved:true + # unique account id, participants + chat_conversation_account = find_or_create_conversation + render json: chat_conversation_account, each_serializer: REST::ChatConversationAccountSerializer + end + + def mark_chat_conversation_unread + @chat_conversation_account.update!(is_unread: true) + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + end + + def mark_chat_conversation_hidden + @chat_conversation_account.update!(is_hidden: true) + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + end + + def mark_chat_conversation_approved + @chat_conversation_account.update!(is_approved: true) + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + end + + private + + def find_or_create_conversation + chat = ChatConversationAccount.find_by(account: current_account, participant_account_ids: [@account.id.to_s]) + + return chat unless chat.nil? + + chat_conversation = ChatConversation.create + + my_chat = ChatConversationAccount.create!( + account: current_account, + participant_account_ids: [@account.id.to_s], + chat_conversation: chat_conversation, + is_approved: true + ) + + # : todo : if multiple ids + their_chat = ChatConversationAccount.create!( + account: @account, + participant_account_ids: [current_account.id.to_s], + chat_conversation: chat_conversation, + is_approved: false # default as request + ) + + return my_chat + end + + def set_account + @account = Account.find(params[:account_id]) + end + + def set_chat_conversation + @chat_conversation = ChatConversation.find(params[:id]) + @chat_conversation_account = ChatConversationAccount.where( + account: current_account, + chat_conversation: @chat_conversation + ).first + end + +end diff --git a/app/controllers/api/v1/chat_conversations/approved_conversations_controller.rb b/app/controllers/api/v1/chat_conversations/approved_conversations_controller.rb new file mode 100644 index 00000000..c606c23b --- /dev/null +++ b/app/controllers/api/v1/chat_conversations/approved_conversations_controller.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversations::ApprovedConversationsController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' } + + before_action :require_user! + after_action :insert_pagination_headers + + def index + puts "tilly ApprovedConversationsController-0" + @chat_conversations = load_chat_conversations + render json: @chat_conversations, each_serializer: REST::ChatConversationAccountSerializer + end + + def show + puts "tilly ApprovedConversationsController-1" + @chat_conversations = load_chat_conversations + render json: @chat_conversations, each_serializer: REST::ChatConversationAccountSerializer + end + + private + + def load_chat_conversations + paginated_chat_conversations + end + + def paginated_chat_conversations + ChatConversationAccount.where( + account: current_account, + is_hidden: false, + is_approved: true + ).paginate_by_max_id( + limit_param(DEFAULT_CHAT_CONVERSATION_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def next_path + if records_continue? + api_v1_chat_conversations_approved_conversations_url pagination_params(max_id: pagination_max_id) + end + end + + def prev_path + unless paginated_chat_conversations.empty? + api_v1_chat_conversations_approved_conversations_url pagination_params(since_id: pagination_since_id) + end + end + + def pagination_max_id + paginated_chat_conversations.last.id + end + + def pagination_since_id + paginated_chat_conversations.first.id + end + + def records_continue? + paginated_chat_conversations.size == limit_param(DEFAULT_CHAT_CONVERSATION_LIMIT) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + +end diff --git a/app/controllers/api/v1/chat_conversations/messages_controller.rb b/app/controllers/api/v1/chat_conversations/messages_controller.rb new file mode 100644 index 00000000..579e6af8 --- /dev/null +++ b/app/controllers/api/v1/chat_conversations/messages_controller.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversations::MessagesController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' } + before_action -> { doorkeeper_authorize! :write, :'write:chats' } + + before_action :require_user! + before_action :set_chat_conversation + before_action :set_chat_messages + + after_action :insert_pagination_headers, unless: -> { @chats.empty? } + + def show + puts "tilly chat_message_conversations - 1: " + @chats.count.inspect + render json: @chats, each_serializer: REST::ChatMessageSerializer + end + + private + + def set_chat_conversation + @chat_conversation = ChatConversation.find(params[:id]) + end + + def set_chat_messages + @chats = cached_conversation_chats + end + + def cached_conversation_chats + cache_collection conversation_chats, ChatMessage + end + + def conversation_chats + chats = ChatMessage.where( + chat_conversation: @chat_conversation + ).paginate_by_id( + limit_param(DEFAULT_CHAT_CONVERSATION_MESSAGE_LIMIT), + params_slice(:max_id, :since_id, :min_id) + ) + end + + def insert_pagination_headers + set_pagination_headers(next_path, nil) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + + def next_path + api_v1_chat_conversations_message_url params[:id], pagination_params(since_id: pagination_since_id) + end + + def pagination_since_id + @chats.first.id + end +end diff --git a/app/controllers/api/v1/chat_conversations/requested_conversations_controller.rb b/app/controllers/api/v1/chat_conversations/requested_conversations_controller.rb new file mode 100644 index 00000000..80acc3a7 --- /dev/null +++ b/app/controllers/api/v1/chat_conversations/requested_conversations_controller.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +class Api::V1::ChatConversations::RequestedConversationsController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' } + + before_action :require_user! + after_action :insert_pagination_headers + + def index + @chat_conversations = load_chat_conversations + render json: @chat_conversations, each_serializer: REST::ChatConversationAccountSerializer + end + + def count + count = ChatConversationAccount.where(account: current_account, is_hidden: false, is_approved: false).count + render json: count + end + + private + + def load_chat_conversations + paginated_chat_conversations + end + + def paginated_chat_conversations + ChatConversationAccount.where( + account: current_account, + is_hidden: false, + is_approved: false + ).paginate_by_max_id( + limit_param(DEFAULT_CHAT_CONVERSATION_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def next_path + if records_continue? + api_v1_chat_conversations_requested_conversations_url pagination_params(max_id: pagination_max_id) + end + end + + def prev_path + unless paginated_chat_conversations.empty? + api_v1_chat_conversations_requested_conversations_url pagination_params(since_id: pagination_since_id) + end + end + + def pagination_max_id + paginated_chat_conversations.last.id + end + + def pagination_since_id + paginated_chat_conversations.first.id + end + + def records_continue? + paginated_chat_conversations.size == limit_param(DEFAULT_CHAT_CONVERSATION_LIMIT) + end + + def pagination_params(core_params) + params.slice(:limit).permit(:limit).merge(core_params) + end + +end diff --git a/app/controllers/api/v1/chat_messages_controller.rb b/app/controllers/api/v1/chat_messages_controller.rb new file mode 100644 index 00000000..8b37b642 --- /dev/null +++ b/app/controllers/api/v1/chat_messages_controller.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class Api::V1::ChatMessagesController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:chats' } + before_action -> { doorkeeper_authorize! :write, :'write:chats' } + + before_action :require_user! + before_action :set_chat_conversation + + def create + @chat = ChatMessage.create!( + from_account: current_account, + chat_conversation: @chat_conversation, + text: params[:text] + ) + + # : todo : + # Redis.current.publish("chat_messages:10", 'hi') + Redis.current.publish("chat_messages:10", Oj.dump(event: :chat_message, payload: InlineRenderer.render(@chat, current_user.account, :chat_message))) + + render json: @chat, serializer: REST::ChatMessageSerializer + end + + def destroy + @chat = ChatMessage.where(account: current_user.account).find(params[:id]) + authorize @chat, :destroy? + + # : todo : + # make sure last_chat_message_id in chat_account_conversation gets set to last + + @chat.destroy! + + render json: @chat, serializer: REST::ChatMessageSerializer + end + + private + + def set_chat_conversation + @chat_conversation = ChatConversation.find(params[:chat_conversation_id]) + end + + def chat_params + params.permit(:text, :chat_conversation_id) + end + +end diff --git a/app/controllers/api/v1/messages/chats_controller.rb b/app/controllers/api/v1/messages/chats_controller.rb deleted file mode 100644 index e69de29b..00000000 diff --git a/app/controllers/api/v1/messages/converations_controller.rb b/app/controllers/api/v1/messages/converations_controller.rb deleted file mode 100644 index e69de29b..00000000 diff --git a/app/controllers/empty_controller.rb b/app/controllers/empty_controller.rb index 8f215dfe..bde3493e 100644 --- a/app/controllers/empty_controller.rb +++ b/app/controllers/empty_controller.rb @@ -48,4 +48,16 @@ class EmptyController < ActionController::Base nil end + protected + + def limit_param(default_limit) + return default_limit unless params[:limit] + [params[:limit].to_i.abs, default_limit * 2].min + end + + def truthy_param?(key) + ActiveModel::Type::Boolean.new.cast(params[key]) + end + + end diff --git a/app/controllers/react_controller.rb b/app/controllers/react_controller.rb index cb7d4822..810abb8a 100644 --- a/app/controllers/react_controller.rb +++ b/app/controllers/react_controller.rb @@ -52,7 +52,7 @@ class ReactController < ApplicationController end def find_route_matches - request.path.match(/\A\/(home|news|suggestions|links|messages|shortcuts|group|groups|list|lists|notifications|tags|compose|follow_requests|admin|account|settings|filters|timeline|blocks|mutes)/) + request.path.match(/\A\/(home|news|api|suggestions|links|chat_conversations|chat_conversation_accounts|messages|shortcuts|group|groups|list|lists|notifications|tags|compose|follow_requests|admin|account|settings|filters|timeline|blocks|mutes)/) end def find_public_route_matches diff --git a/app/javascript/gabsocial/actions/accounts.js b/app/javascript/gabsocial/actions/accounts.js index dd752a25..e8b53e96 100644 --- a/app/javascript/gabsocial/actions/accounts.js +++ b/app/javascript/gabsocial/actions/accounts.js @@ -163,6 +163,7 @@ export const followAccount = (id, reblogs = true) => (dispatch, getState) => { dispatch(followAccountRequest(id, locked)) api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then((response) => { + console.log("response:", response) dispatch(followAccountSuccess(response.data, alreadyFollowing)) }).catch((error) => { dispatch(followAccountFail(error, locked)) diff --git a/app/javascript/gabsocial/actions/chat_compose.js b/app/javascript/gabsocial/actions/chat_compose.js deleted file mode 100644 index ac85f788..00000000 --- a/app/javascript/gabsocial/actions/chat_compose.js +++ /dev/null @@ -1,28 +0,0 @@ -import api from '../api' -import { me } from '../initial_state' - -export const MESSAGE_INPUT_CHANGE = 'MESSAGE_INPUT_CHANGE' -export const MESSAGE_INPUT_RESET = 'MESSAGE_INPUT_RESET' - -/** - * - */ -export const messageInputChange = (text) => (dispatch, getState) => { - if (!me) return - - //Ensure has conversation - const conversationId = getState().getIn(['chat_conversations', 'current', 'conversation_id'], null) - if (!conversationId) return - - dispatch({ - type: MESSAGE_INPUT_CHANGE, - text, - }) -} - -/** - * - */ -export const messageInputReset = (dispatch) => { - dispatch({ type: MESSAGE_INPUT_RESET }) -} diff --git a/app/javascript/gabsocial/actions/chat_conversation_messages.js b/app/javascript/gabsocial/actions/chat_conversation_messages.js new file mode 100644 index 00000000..2e8cb4ec --- /dev/null +++ b/app/javascript/gabsocial/actions/chat_conversation_messages.js @@ -0,0 +1,111 @@ +import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable' +import noop from 'lodash.noop' +import api, { getLinks } from '../api' +import { me } from '../initial_state' +import { importFetchedChatMessages } from './importer' + +export const CHAT_CONVERSATION_MESSAGES_EXPAND_REQUEST = 'CHAT_CONVERSATION_MESSAGES_EXPAND_REQUEST' +export const CHAT_CONVERSATION_MESSAGES_EXPAND_SUCCESS = 'CHAT_CONVERSATION_MESSAGES_EXPAND_SUCCESS' +export const CHAT_CONVERSATION_MESSAGES_EXPAND_FAIL = 'CHAT_CONVERSATION_MESSAGES_EXPAND_FAIL' + +// + +export const CHAT_CONVERSATION_MESSAGES_CONNECT = 'CHAT_CONVERSATION_MESSAGES_CONNECT' +export const CHAT_CONVERSATION_MESSAGES_DISCONNECT = 'CHAT_CONVERSATION_MESSAGES_DISCONNECT' +export const CHAT_CONVERSATION_MESSAGES_CLEAR = 'CHAT_CONVERSATION_MESSAGES_CLEAR' +export const CHAT_CONVERSATION_MESSAGES_SCROLL_BOTTOM = 'CHAT_CONVERSATION_MESSAGES_SCROLL_BOTTOM' + +/** + * + */ +export const connectChatMessageConversation = (chatConversationId) => ({ + type: CHAT_CONVERSATION_MESSAGES_CONNECT, + chatConversationId, +}) + +/** + * + */ +export const disconnectChatMessageConversation = (chatConversationId) => ({ + type: CHAT_CONVERSATION_MESSAGES_DISCONNECT, + chatConversationId, +}) + +/** + * + */ +export const clearChatMessageConversation = (chatConversationId) => (dispatch) => { + dispatch({ + type: CHAT_CONVERSATION_MESSAGES_CLEAR, + chatConversationId + }) +} + +/** + * + */ +export const scrollBottomChatMessageConversation = (chatConversationId, top) => ({ + type: CHAT_CONVERSATION_MESSAGES_SCROLL_BOTTOM, + chatConversationId, + top, +}) + +/** + * + */ +export const expandChatMessages = (chatConversationId, params = {}, done = noop) => (dispatch, getState) => { + if (!me || !chatConversationId) return + + const chatConversation = getState().getIn(['chat_messages', chatConversationId], ImmutableMap()) + const isLoadingMore = !!params.maxId + + if (!!chatConversation && (chatConversation.get('isLoading') || chatConversation.get('isError'))) { + done() + return + } + + if (!params.maxId && chatConversation.get('items', ImmutableList()).size > 0) { + params.sinceId = chatConversation.getIn(['items', 0]) + } + + const isLoadingRecent = !!params.sinceId + + dispatch(expandChatMessagesRequest(chatConversationId, isLoadingMore)) + + api(getState).get(`/api/v1/chat_conversations/messages/${chatConversationId}`, { params }).then((response) => { + console.log("response:", response) + const next = getLinks(response).refs.find(link => link.rel === 'next') + console.log("next:", next, getLinks(response).refs) + dispatch(importFetchedChatMessages(response.data)) + dispatch(expandChatMessagesSuccess(chatConversationId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore)) + done() + }).catch((error) => { + console.log("error:", error) + dispatch(expandChatMessagesFail(chatConversationId, error, isLoadingMore)) + done() + }) +} + +export const expandChatMessagesRequest = (chatConversationId, isLoadingMore) => ({ + type: CHAT_CONVERSATION_MESSAGES_EXPAND_REQUEST, + chatConversationId, + skipLoading: !isLoadingMore, +}) + +export const expandChatMessagesSuccess = (chatConversationId, chatMessages, next, partial, isLoadingRecent, isLoadingMore) => ({ + type: CHAT_CONVERSATION_MESSAGES_EXPAND_SUCCESS, + chatConversationId, + chatMessages, + next, + partial, + isLoadingRecent, + skipLoading: !isLoadingMore, +}) + +export const expandChatMessagesFail = (chatConversationId, error, isLoadingMore) => ({ + type: CHAT_CONVERSATION_MESSAGES_EXPAND_FAIL, + chatConversationId, + error, + skipLoading: !isLoadingMore, +}) + diff --git a/app/javascript/gabsocial/actions/chat_conversations.js b/app/javascript/gabsocial/actions/chat_conversations.js index 6263209f..4a594ac3 100644 --- a/app/javascript/gabsocial/actions/chat_conversations.js +++ b/app/javascript/gabsocial/actions/chat_conversations.js @@ -3,13 +3,35 @@ import { fetchRelationships } from './accounts' import { importFetchedAccounts } from './importer' import { me } from '../initial_state' -export const CONVERSATION_BLOCKS_FETCH_REQUEST = 'CONVERSATION_BLOCKS_FETCH_REQUEST' -export const CONVERSATION_BLOCKS_FETCH_SUCCESS = 'CONVERSATION_BLOCKS_FETCH_SUCCESS' -export const CONVERSATION_BLOCKS_FETCH_FAIL = 'CONVERSATION_BLOCKS_FETCH_FAIL' +// -export const CONVERSATION_BLOCKS_EXPAND_REQUEST = 'CONVERSATION_BLOCKS_EXPAND_REQUEST' -export const CONVERSATION_BLOCKS_EXPAND_SUCCESS = 'CONVERSATION_BLOCKS_EXPAND_SUCCESS' -export const CONVERSATION_BLOCKS_EXPAND_FAIL = 'CONVERSATION_BLOCKS_EXPAND_FAIL' +export const CHAT_CONVERSATIONS_APPROVED_FETCH_REQUEST = 'CHAT_CONVERSATIONS_APPROVED_FETCH_REQUEST' +export const CHAT_CONVERSATIONS_APPROVED_FETCH_SUCCESS = 'CHAT_CONVERSATIONS_APPROVED_FETCH_SUCCESS' +export const CHAT_CONVERSATIONS_APPROVED_FETCH_FAIL = 'CHAT_CONVERSATIONS_APPROVED_FETCH_FAIL' + +export const CHAT_CONVERSATIONS_APPROVED_EXPAND_REQUEST = 'CHAT_CONVERSATIONS_APPROVED_EXPAND_REQUEST' +export const CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS = 'CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS' +export const CHAT_CONVERSATIONS_APPROVED_EXPAND_FAIL = 'CHAT_CONVERSATIONS_APPROVED_EXPAND_FAIL' + +// + +export const CHAT_CONVERSATIONS_CREATE_REQUEST = 'CHAT_CONVERSATIONS_CREATE_REQUEST' +export const CHAT_CONVERSATIONS_CREATE_SUCCESS = 'CHAT_CONVERSATIONS_CREATE_SUCCESS' +export const CHAT_CONVERSATIONS_CREATE_FAIL = 'CHAT_CONVERSATIONS_CREATE_FAIL' + +export const CHAT_CONVERSATIONS_DELETE_REQUEST = 'CHAT_CONVERSATIONS_DELETE_REQUEST' +export const CHAT_CONVERSATIONS_DELETE_SUCCESS = 'CHAT_CONVERSATIONS_DELETE_SUCCESS' +export const CHAT_CONVERSATIONS_DELETE_FAIL = 'CHAT_CONVERSATIONS_DELETE_FAIL' + +// + +export const CHAT_CONVERSATION_BLOCKS_FETCH_REQUEST = 'CHAT_CONVERSATION_BLOCKS_FETCH_REQUEST' +export const CHAT_CONVERSATION_BLOCKS_FETCH_SUCCESS = 'CHAT_CONVERSATION_BLOCKS_FETCH_SUCCESS' +export const CHAT_CONVERSATION_BLOCKS_FETCH_FAIL = 'CHAT_CONVERSATION_BLOCKS_FETCH_FAIL' + +export const CHAT_CONVERSATION_BLOCKS_EXPAND_REQUEST = 'CHAT_CONVERSATION_BLOCKS_EXPAND_REQUEST' +export const CHAT_CONVERSATION_BLOCKS_EXPAND_SUCCESS = 'CHAT_CONVERSATION_BLOCKS_EXPAND_SUCCESS' +export const CHAT_CONVERSATION_BLOCKS_EXPAND_FAIL = 'CHAT_CONVERSATION_BLOCKS_EXPAND_FAIL' export const BLOCK_MESSAGER_REQUEST = 'BLOCK_MESSAGER_REQUEST' export const BLOCK_MESSAGER_SUCCESS = 'BLOCK_MESSAGER_SUCCESS' @@ -21,13 +43,13 @@ export const UNBLOCK_MESSAGER_FAIL = 'UNBLOCK_MESSAGER_FAIL' // -export const CONVERSATION_MUTES_FETCH_REQUEST = 'CONVERSATION_MUTES_FETCH_REQUEST' -export const CONVERSATION_MUTES_FETCH_SUCCESS = 'CONVERSATION_MUTES_FETCH_SUCCESS' -export const CONVERSATION_MUTES_FETCH_FAIL = 'CONVERSATION_MUTES_FETCH_FAIL' +export const CHAT_CONVERSATION_MUTES_FETCH_REQUEST = 'CHAT_CONVERSATION_MUTES_FETCH_REQUEST' +export const CHAT_CONVERSATION_MUTES_FETCH_SUCCESS = 'CHAT_CONVERSATION_MUTES_FETCH_SUCCESS' +export const CHAT_CONVERSATION_MUTES_FETCH_FAIL = 'CHAT_CONVERSATION_MUTES_FETCH_FAIL' -export const CONVERSATION_MUTES_EXPAND_REQUEST = 'CONVERSATION_MUTES_EXPAND_REQUEST' -export const CONVERSATION_MUTES_EXPAND_SUCCESS = 'CONVERSATION_MUTES_EXPAND_SUCCESS' -export const CONVERSATION_MUTES_EXPAND_FAIL = 'CONVERSATION_MUTES_EXPAND_FAIL' +export const CHAT_CONVERSATION_MUTES_EXPAND_REQUEST = 'CHAT_CONVERSATION_MUTES_EXPAND_REQUEST' +export const CHAT_CONVERSATION_MUTES_EXPAND_SUCCESS = 'CHAT_CONVERSATION_MUTES_EXPAND_SUCCESS' +export const CHAT_CONVERSATION_MUTES_EXPAND_FAIL = 'CHAT_CONVERSATION_MUTES_EXPAND_FAIL' export const MUTE_MESSAGER_REQUEST = 'BLOCK_MESSAGER_REQUEST' export const MUTE_MESSAGER_SUCCESS = 'BLOCK_MESSAGER_SUCCESS' @@ -39,25 +61,238 @@ export const UNMUTE_MESSAGER_FAIL = 'UNMUTE_MESSAGER_FAIL' // -export const CONVERSATION_REQUEST_APPROVE_SUCCESS = 'CONVERSATION_REQUEST_APPROVE_SUCCESS' -export const CONVERSATION_REQUEST_APPROVE_FAIL = 'CONVERSATION_REQUEST_APPROVE_FAIL' +export const CHAT_CONVERSATION_REQUESTED_COUNT_FETCH_SUCCESS = 'CHAT_CONVERSATION_REQUESTED_COUNT_FETCH_SUCCESS' -export const CONVERSATION_REQUEST_REJECT_SUCCESS = 'CONVERSATION_REQUEST_REJECT_SUCCESS' -export const CONVERSATION_REQUEST_REJECT_FAIL = 'CONVERSATION_REQUEST_REJECT_FAIL' +export const CHAT_CONVERSATIONS_REQUESTED_FETCH_REQUEST = 'CHAT_CONVERSATIONS_REQUESTED_FETCH_REQUEST' +export const CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS = 'CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS' +export const CHAT_CONVERSATIONS_REQUESTED_FETCH_FAIL = 'CHAT_CONVERSATIONS_REQUESTED_FETCH_FAIL' -export const CONVERSATION_DELETE_REQUEST = 'CONVERSATION_DELETE_REQUEST' -export const CONVERSATION_DELETE_SUCCESS = 'CONVERSATION_DELETE_SUCCESS' -export const CONVERSATION_DELETE_FAIL = 'CONVERSATION_DELETE_FAIL' +export const CHAT_CONVERSATIONS_REQUESTED_EXPAND_REQUEST = 'CHAT_CONVERSATIONS_REQUESTED_EXPAND_REQUEST' +export const CHAT_CONVERSATIONS_REQUESTED_EXPAND_SUCCESS = 'CHAT_CONVERSATIONS_REQUESTED_EXPAND_SUCCESS' +export const CHAT_CONVERSATIONS_REQUESTED_EXPAND_FAIL = 'CHAT_CONVERSATIONS_REQUESTED_EXPAND_FAIL' // -export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST' -export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS' -export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL' +export const CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS = 'CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS' +export const CHAT_CONVERSATION_REQUEST_APPROVE_FAIL = 'CHAT_CONVERSATION_REQUEST_APPROVE_FAIL' + +export const CHAT_CONVERSATION_DELETE_REQUEST = 'CHAT_CONVERSATION_DELETE_REQUEST' +export const CHAT_CONVERSATION_DELETE_SUCCESS = 'CHAT_CONVERSATION_DELETE_SUCCESS' +export const CHAT_CONVERSATION_DELETE_FAIL = 'CHAT_CONVERSATION_DELETE_FAIL' + +/** + * + */ +export const fetchChatConversations = () => (dispatch, getState) => { + if (!me) return + + dispatch(fetchChatConversationsRequest()) + + api(getState).get('/api/v1/chat_conversations/approved_conversations').then((response) => { + console.log("chat_conversations response: ", response) + + const next = getLinks(response).refs.find(link => link.rel === 'next') + const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts)) + const conversationsChatMessages = response.data.map((c) => c.last_chat_message) + + dispatch(importFetchedAccounts(conversationsAccounts)) + // dispatch(importFetchedChatMessages(conversationsChatMessages)) + dispatch(fetchChatConversationsSuccess(response.data, next ? next.uri : null)) + }).catch((error) => { + console.log("fetchChatConversationsFail:", error) + dispatch(fetchChatConversationsFail(error)) + }) +} + +export const fetchChatConversationsRequest = () => ({ + type: CHAT_CONVERSATIONS_APPROVED_FETCH_REQUEST, +}) + +export const fetchChatConversationsSuccess = (chatConversations, next) => ({ + type: CHAT_CONVERSATIONS_APPROVED_FETCH_SUCCESS, + chatConversations, + next, +}) + +export const fetchChatConversationsFail = (error) => ({ + type: CHAT_CONVERSATIONS_APPROVED_FETCH_FAIL, + error, +}) + +/** + * + */ +export const expandChatConversations = () => (dispatch, getState) => { + if (!me) return + + const url = getState().getIn(['chat_conversations', 'approved', 'next']) + const isLoading = getState().getIn(['chat_conversations', 'approved', 'isLoading']) + + if (url === null || isLoading) return + + dispatch(expandChatConversationsRequest()) + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next') + const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts)) + const conversationsChatMessages = response.data.map((c) => c.last_chat_message) + + dispatch(importFetchedAccounts(conversationsAccounts)) + // dispatch(importFetchedChatMessages(conversationsChatMessages)) + dispatch(expandChatConversationsSuccess(response.data, next ? next.uri : null)) + }).catch(error => dispatch(expandChatConversationsFail(error))) +} + +export const expandChatConversationsRequest = () => ({ + type: CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS, +}) + +export const expandChatConversationsSuccess = (chatConversations, next) => ({ + type: CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS, + chatConversations, + next, +}) + +export const expandChatConversationsFail = (error) => ({ + type: CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS, + error, +}) + +/** + * + */ +export const fetchChatConversationRequested = () => (dispatch, getState) => { + if (!me) return + + dispatch(fetchChatConversationRequestedRequest()) + + api(getState).get('/api/v1/chat_conversations/requested_conversations').then((response) => { + const next = getLinks(response).refs.find(link => link.rel === 'next') + const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts)) + const conversationsChatMessages = response.data.map((c) => c.last_chat_message) + + dispatch(importFetchedAccounts(conversationsAccounts)) + // dispatch(importFetchedChatMessages(conversationsChatMessages)) + dispatch(fetchChatConversationRequestedSuccess(response.data, next ? next.uri : null)) + }).catch((error) => { + console.log("error:", error) + dispatch(fetchChatConversationRequestedFail(error)) + }) +} + +export const fetchChatConversationRequestedRequest = () => ({ + type: CHAT_CONVERSATIONS_REQUESTED_FETCH_REQUEST, +}) + +export const fetchChatConversationRequestedSuccess = (chatConversations, next) => ({ + type: CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS, + chatConversations, + next, +}) + +export const fetchChatConversationRequestedFail = (error) => ({ + type: CHAT_CONVERSATIONS_REQUESTED_FETCH_FAIL, + error, +}) + +/** + * + */ +export const expandChatConversationRequested = () => (dispatch, getState) => { + if (!me) return + + const url = getState().getIn(['chat_conversations', 'requested', 'next']) + const isLoading = getState().getIn(['chat_conversations', 'requested', 'isLoading']) + + if (url === null || isLoading) return + + dispatch(expandChatConversationRequestedRequest()) + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next') + const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts)) + const conversationsChatMessages = response.data.map((c) => c.last_chat_message) + + dispatch(importFetchedAccounts(conversationsAccounts)) + // dispatch(importFetchedChatMessages(conversationsChatMessages)) + dispatch(expandChatConversationRequestedSuccess(response.data, next ? next.uri : null)) + }).catch(error => dispatch(expandChatConversationRequestedFail(error))) +} + +export const expandChatConversationRequestedRequest = () => ({ + type: CHAT_CONVERSATIONS_REQUESTED_EXPAND_REQUEST, +}) + +export const expandChatConversationRequestedSuccess = (chatConversations, next) => ({ + type: CHAT_CONVERSATIONS_REQUESTED_EXPAND_SUCCESS, + chatConversations, + next, +}) + +export const expandChatConversationRequestedFail = (error) => ({ + type: CHAT_CONVERSATIONS_REQUESTED_EXPAND_FAIL, + error, +}) + +/** + * + */ +export const createChatConversation = (accountId) => (dispatch, getState) => { + if (!me || !accountId) return + + dispatch(createChatConversationRequest()) + + api(getState).post('/api/v1/chat_conversation', { account_id: accountId }).then((response) => { + dispatch(createChatConversationSuccess(response.data)) + }).catch((error) => { + dispatch(createChatConversationFail(error)) + }) +} + +export const createChatConversationRequest = () => ({ + type: CHAT_CONVERSATIONS_CREATE_REQUEST, +}) + +export const createChatConversationSuccess = (chatConversation) => ({ + type: CHAT_CONVERSATIONS_CREATE_SUCCESS, + chatConversation, +}) + +export const createChatConversationFail = (error) => ({ + type: CHAT_CONVERSATIONS_CREATE_FAIL, + error, +}) + +/** + * + */ +export const deleteChatConversation = (chatConversationId) => (dispatch, getState) => { + if (!me || !chatConversationId) return + + dispatch(deleteChatConversationRequest(conversationId)) + + api(getState).delete(`/api/v1/chat_conversation/${chatConversationId}`).then((response) => { + console.log("chat_conversations delete response: ", response) + dispatch(deleteChatConversationSuccess()) + }).catch((error) => { + dispatch(deleteChatConversationFail(error)) + }) +} + +export const deleteChatConversationRequest = (conversationId) => ({ + type: CHAT_CONVERSATIONS_DELETE_REQUEST, + conversationId, +}) + +export const deleteChatConversationSuccess = () => ({ + type: CHAT_CONVERSATIONS_DELETE_SUCCESS, +}) + +export const deleteChatConversationFail = (error) => ({ + type: CHAT_CONVERSATIONS_DELETE_FAIL, + error, +}) -export const CONVERSATIONS_EXPAND_REQUEST = 'CONVERSATIONS_EXPAND_REQUEST' -export const CONVERSATIONS_EXPAND_SUCCESS = 'CONVERSATIONS_EXPAND_SUCCESS' -export const CONVERSATIONS_EXPAND_FAIL = 'CONVERSATIONS_EXPAND_FAIL' /** * @@ -110,12 +345,12 @@ const unblockMessengerRequest = (accountId) => ({ accountId, }) -const blockMessengerSuccess = (data) => ({ +const unblockMessengerSuccess = (data) => ({ type: UNBLOCK_MESSAGER_REQUEST, data, }) -const blockMessengerFail = (accountId, error) => ({ +const unblockMessengerFail = (accountId, error) => ({ type: UNBLOCK_MESSAGER_REQUEST, accountId, error, @@ -137,20 +372,20 @@ export const fetchBlocks = () => (dispatch, getState) => { }).catch(error => dispatch(fetchBlocksFail(error))) } -export const fetchBlocksRequest = () => ({ - type: BLOCKS_FETCH_REQUEST, -}) +// export const fetchBlocksRequest = () => ({ +// type: BLOCKS_FETCH_REQUEST, +// }) -export const fetchBlocksSuccess = (accounts, next) => ({ - type: BLOCKS_FETCH_SUCCESS, - accounts, - next, -}) +// export const fetchBlocksSuccess = (accounts, next) => ({ +// type: BLOCKS_FETCH_SUCCESS, +// accounts, +// next, +// }) -export const fetchBlocksFail = (error) => ({ - type: BLOCKS_FETCH_FAIL, - error, -}) +// export const fetchBlocksFail = (error) => ({ +// type: BLOCKS_FETCH_FAIL, +// error, +// }) /** * @@ -173,17 +408,51 @@ export const expandBlocks = () => (dispatch, getState) => { }).catch(error => dispatch(expandBlocksFail(error))) } -export const expandBlocksRequest = () => ({ - type: BLOCKS_EXPAND_REQUEST, +// export const expandBlocksRequest = () => ({ +// type: BLOCKS_EXPAND_REQUEST, +// }) + +// export const expandBlocksSuccess = (accounts, next) => ({ +// type: BLOCKS_EXPAND_SUCCESS, +// accounts, +// next, +// }) + +// export const expandBlocksFail = (error) => ({ +// type: BLOCKS_EXPAND_FAIL, +// error, +// }) + +/** + * + */ +export const fetchChatConversationRequestedCount = () => (dispatch, getState) => { + if (!me) return + + api(getState).get('/api/v1/chat_conversations/requested_conversations/count').then(response => { + dispatch({ + type: CHAT_CONVERSATION_REQUESTED_COUNT_FETCH_SUCCESS, + count: response.data, + }) + }) +} + +/** + * + */ +export const approveChatConversationRequest = (chatConversationId) => (dispatch, getState) => { + if (!me|| !chatConversationId) return + + api(getState).post(`/api/v1/chat_conversation/${chatConversationId}/mark_chat_conversation_approved`).then((response) => { + dispatch(approveChatConversationRequestSuccess(response.data)) + }).catch((error) => dispatch(approveChatConversationRequestFail(error))) +} + +export const approveChatConversationRequestSuccess = (chatConversation) => ({ + type: CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS, + chatConversation, }) -export const expandBlocksSuccess = (accounts, next) => ({ - type: BLOCKS_EXPAND_SUCCESS, - accounts, - next, -}) - -export const expandBlocksFail = (error) => ({ - type: BLOCKS_EXPAND_FAIL, - error, -}) +export const approveChatConversationRequestFail = () => ({ + type: CHAT_CONVERSATION_REQUEST_APPROVE_FAIL, +}) \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/chat_messages.js b/app/javascript/gabsocial/actions/chat_messages.js index e65912c9..3c89cac0 100644 --- a/app/javascript/gabsocial/actions/chat_messages.js +++ b/app/javascript/gabsocial/actions/chat_messages.js @@ -1,86 +1,85 @@ +import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable' +import noop from 'lodash.noop' import api from '../api' import { me } from '../initial_state' +import { importFetchedChatMessages } from './importer' -export const MESSAGE_SEND_REQUEST = 'MESSAGE_SEND_REQUEST' -export const MESSAGE_SEND_SUCCESS = 'MESSAGE_SEND_SUCCESS' -export const MESSAGE_SEND_FAIL = 'MESSAGE_SEND_FAIL' +export const CHAT_MESSAGES_SEND_REQUEST = 'CHAT_MESSAGES_SEND_REQUEST' +export const CHAT_MESSAGES_SEND_SUCCESS = 'CHAT_MESSAGES_SEND_SUCCESS' +export const CHAT_MESSAGES_SEND_FAIL = 'CHAT_MESSAGES_SEND_FAIL' -export const MESSAGE_DELETE_REQUEST = 'MESSAGE_DELETE_REQUEST' -export const MESSAGE_DELETE_SUCCESS = 'MESSAGE_DELETE_SUCCESS' -export const MESSAGE_DELETE_FAIL = 'MESSAGE_DELETE_FAIL' +export const CHAT_MESSAGES_DELETE_REQUEST = 'CHAT_MESSAGES_DELETE_REQUEST' +export const CHAT_MESSAGES_DELETE_SUCCESS = 'CHAT_MESSAGES_DELETE_SUCCESS' +export const CHAT_MESSAGES_DELETE_FAIL = 'CHAT_MESSAGES_DELETE_FAIL' /** * */ -const sendMessage = (text, conversationId) => (dispatch, getState) => { - if (!me) return +export const sendChatMessage = (text = '', chatConversationId) => (dispatch, getState) => { + if (!me || !chatConversationId) return + if (text.length === 0) return - // : todo : - // let text = getState().getIn(['chat_messages', 'text'], '') - // let conversationId = getState().getIn(['chat_messags', 'conversation_id'], '') - dispatch(sendMessageRequest()) - api(getState).put('/api/v1/messages/chat', { + api(getState).post('/api/v1/chat_messages', { text, - conversationId, + chat_conversation_id: chatConversationId, }, { - headers: { - 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), - }, + // headers: { + // 'Idempotency-Key': getState().getIn(['chat_compose`', 'idempotencyKey']), + // }, }).then((response) => { - sendMessageSuccess(response) + dispatch(importFetchedChatMessages([response.data])) + dispatch(sendMessageSuccess(response.data, chatConversationId)) }).catch((error) => { dispatch(sendMessageFail(error)) }) } -const sendMessageRequest = (text, conversationId) => ({ - type: MESSAGE_SEND_REQUEST, - text, - conversationId, +const sendMessageRequest = () => ({ + type: CHAT_MESSAGES_SEND_REQUEST, }) -const sendMessageSuccess = () => ({ - type: MESSAGE_SEND_SUCCESS, +const sendMessageSuccess = (chatMessage, chatConversationId) => ({ + type: CHAT_MESSAGES_SEND_SUCCESS, + chatMessage, + chatConversationId, }) const sendMessageFail = (error) => ({ - type: MESSAGE_SEND_FAIL, + type: CHAT_MESSAGES_SEND_FAIL, error, }) /** * */ -const deleteMessage = (messageId) => (dispatch, getState) => { - if (!me || !messageId) return +const deleteMessage = (chatMessageId) => (dispatch, getState) => { + if (!me || !chatMessageId) return - // : todo : - - dispatch(sendMessageRequest()) + dispatch(deleteMessageRequest(chatMessageId)) - api(getState).delete(`/api/v1/messages/chat/${messageId}`, {}, { - headers: { - 'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']), - }, + api(getState).delete(`/api/v1/chat_messages/${chatMessageId}`, {}, { + // headers: { + // 'Idempotency-Key': getState().getIn(['chat_compose', 'idempotencyKey']), + // }, }).then((response) => { - sendMessageSuccess(response) + deleteMessageSuccess(response) }).catch((error) => { - dispatch(sendMessageFail(error)) + dispatch(deleteMessageFail(error)) }) } -const deleteMessageRequest = (messageId) => ({ - type: MESSAGE_DELETE_REQUEST, - messageId, +const deleteMessageRequest = (chatMessageId) => ({ + type: CHAT_MESSAGES_DELETE_REQUEST, + chatMessageId, }) const deleteMessageSuccess = () => ({ - type: MESSAGE_DELETE_SUCCESS, + type: CHAT_MESSAGES_DELETE_SUCCESS, }) const deleteMessageFail = (error) => ({ - type: MESSAGE_DELETE_FAIL, + type: CHAT_MESSAGES_DELETE_FAIL, error, }) \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/chats.js b/app/javascript/gabsocial/actions/chats.js new file mode 100644 index 00000000..2aeff5a7 --- /dev/null +++ b/app/javascript/gabsocial/actions/chats.js @@ -0,0 +1,41 @@ +import throttle from 'lodash.throttle' +import api, { getLinks } from '../api' +import { importFetchedAccounts } from './importer' +import { me } from '../initial_state' + +export const CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS_SUCCESS = 'CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS_SUCCESS' + +export const SET_CHAT_CONVERSATION_SELECTED = 'SET_CHAT_CONVERSATION_SELECTED' + +/** + * + */ +export const fetchChatConversationAccountSuggestions = (query) => throttle((dispatch, getState) => { + api(getState).get('/api/v1/accounts/search', { + params: { + q: query, + resolve: false, + limit: 4, + }, + }).then((response) => { + dispatch(importFetchedAccounts(response.data)) + dispatch(fetchChatConversationAccountSuggestionsSuccess(response.data)) + }).catch((error) => { + // + }) +}, 200, { leading: true, trailing: true }) + +const fetchChatConversationAccountSuggestionsSuccess = (accounts) => ({ + type: CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS_SUCCESS, + accounts, +}) + +/** + * + */ +export const setChatConversationSelected = (chatConversationId) => (dispatch) => { + dispatch({ + type: SET_CHAT_CONVERSATION_SELECTED, + chatConversationId, + }) +} \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/importer/index.js b/app/javascript/gabsocial/actions/importer/index.js index c98a256b..e3caee22 100644 --- a/app/javascript/gabsocial/actions/importer/index.js +++ b/app/javascript/gabsocial/actions/importer/index.js @@ -12,6 +12,7 @@ export const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT' export const STATUS_IMPORT = 'STATUS_IMPORT' export const STATUSES_IMPORT = 'STATUSES_IMPORT' export const POLLS_IMPORT = 'POLLS_IMPORT' +export const CHAT_MESSAGES_IMPORT = 'CHAT_MESSAGES_IMPORT' export const ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP = 'ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP' /** @@ -48,6 +49,11 @@ export const importPolls = (polls) => ({ polls, }) +export const importChatMessages = (chatMessages) => ({ + type: CHAT_MESSAGES_IMPORT, + chatMessages, +}) + export const importFetchedAccount = (account) => { return importFetchedAccounts([account]); } @@ -97,7 +103,7 @@ export const importFetchedStatuses = (statuses) => (dispatch, getState) => { } } - statuses.forEach(processStatus) + statuses.forEach(processStatus) dispatch(importPolls(polls)) dispatch(importFetchedAccounts(accounts)) @@ -113,3 +119,7 @@ export const importErrorWhileFetchingAccountByUsername = (username) => ({ type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, username }) + +export const importFetchedChatMessages = (chatMessages) => (dispatch, getState) => { + dispatch(importChatMessages(chatMessages)) +} \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/lists.js b/app/javascript/gabsocial/actions/lists.js index 27d77873..03c85ced 100644 --- a/app/javascript/gabsocial/actions/lists.js +++ b/app/javascript/gabsocial/actions/lists.js @@ -297,7 +297,7 @@ export const fetchListSuggestions = (q) => (dispatch, getState) => { /** * */ -export const fetchListSuggestionsReady = (query, accounts) => ({ +const fetchListSuggestionsReady = (query, accounts) => ({ type: LIST_EDITOR_SUGGESTIONS_READY, query, accounts, diff --git a/app/javascript/gabsocial/actions/streaming.js b/app/javascript/gabsocial/actions/streaming.js index e33caeb5..592b9e51 100644 --- a/app/javascript/gabsocial/actions/streaming.js +++ b/app/javascript/gabsocial/actions/streaming.js @@ -75,15 +75,18 @@ export const connectUserStream = () => connectTimelineStream('home', 'user') /** * */ -export const connectMessageStream = () => { - - return connectStream('chat_messages', null, (dispatch, getState) => { - +export const connectChatMessagesStream = (accountId) => { + return connectStream(`chat_messages:${accountId}`, null, (dispatch, getState) => { return { - onConnect() {}, - onDisconnect() {}, + onConnect() { + // console.log("chat messages connected") + }, + onDisconnect() { + // console.log("chat messages disconnected") + }, onReceive (data) { - // + // : todo : + console.log("chat messages onReceive:", data) }, } }) diff --git a/app/javascript/gabsocial/components/account.js b/app/javascript/gabsocial/components/account.js index 45f17804..9e586896 100644 --- a/app/javascript/gabsocial/components/account.js +++ b/app/javascript/gabsocial/components/account.js @@ -24,10 +24,13 @@ import Avatar from './avatar' import DisplayName from './display_name' import Button from './button' import Text from './text' + class Account extends ImmutablePureComponent { handleAction = (e) => { this.props.onActionClick(this.props.account, e) + e.preventDefault() + return false } handleUnrequest = () => { diff --git a/app/javascript/gabsocial/components/avatar_group.js b/app/javascript/gabsocial/components/avatar_group.js new file mode 100644 index 00000000..16b2e626 --- /dev/null +++ b/app/javascript/gabsocial/components/avatar_group.js @@ -0,0 +1,61 @@ +import React from 'react' +import PropTypes from 'prop-types' +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import Image from './image' + + +/** + * Renders an avatar component + * @param {list} [props.accounts] - the accounts for images + * @param {number} [props.size=40] - the size of the avatar + */ +class AvatarGroup extends ImmutablePureComponent { + + render() { + const { accounts, size } = this.props + + return ( +