Progress on chat conversation search, scrolling

This commit is contained in:
mgabdev 2020-12-22 13:38:52 -05:00
parent 15df66b234
commit 7a62adea3e
13 changed files with 96 additions and 50 deletions

View File

@ -7,7 +7,7 @@ class Api::V1::ChatConversationAccountsController < Api::BaseController
before_action :require_user! before_action :require_user!
before_action :set_account, only: [:block_messenger, :unblock_messenger, :messenger_block_relationships] before_action :set_account, only: [:block_messenger, :unblock_messenger, :messenger_block_relationships]
before_action :check_account_suspension, only: [:block_messenger, :unblock_messenger, :messenger_block_relationships] before_action :check_account_suspension, only: [:block_messenger, :unblock_messenger, :messenger_block_relationships]
before_action :set_chat_conversation, except: [:block_messenger, :unblock_messenger, :messenger_block_relationships] before_action :set_chat_conversation, except: [:block_messenger, :unblock_messenger, :messenger_block_relationships, :search]
def block_messenger def block_messenger
@block = BlockChatMessengerService.new.call(current_user.account, @account) @block = BlockChatMessengerService.new.call(current_user.account, @account)
@ -42,6 +42,13 @@ class Api::V1::ChatConversationAccountsController < Api::BaseController
render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
end end
def search
# : todo :
search_conversations = [] #ChatConversationAccount.where(account: current_account, is_hidden: false, is_approved: true).map(&:participant_account_ids)
# .joins(:account).where("accounts.display_name ILIKE ?", "%#{params[:q]}%")
render json: search_conversations, each_serializer: REST::ChatConversationAccountSerializer
end
private private
def set_account def set_account

View File

@ -34,13 +34,8 @@ class Api::V1::ChatConversationController < Api::BaseController
end end
def mark_chat_conversation_approved def mark_chat_conversation_approved
approved_conversation_count = ChatConversationAccount.where(account: @account, is_hidden: false, is_approved: true).count @chat_conversation_account.update!(is_approved: true)
if approved_conversation_count >= ChatConversationAccount::PER_ACCOUNT_APPROVED_LIMIT render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
render json: { error: true, message: "You have #{approved_conversation_count} active chat conversations. The limit is #{ChatConversationAccount::PER_ACCOUNT_APPROVED_LIMIT}. Delete some conversations first before approving any more requests." }
else
@chat_conversation_account.update!(is_approved: true)
render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
end
end end
def set_expiration_policy def set_expiration_policy

View File

@ -246,4 +246,4 @@ const unmuteChatConversationFail = (accountId, error) => ({
showToast: true, showToast: true,
accountId, accountId,
error, error,
}) })

View File

@ -509,40 +509,29 @@ export const setChatConversationExpirationFail = (error) => ({
error, error,
}) })
/** /**
* *
*/ */
export const fetchChatConversationAccountSuggestions = (query) => (dispatch, getState) => { export const searchApprovedChatConversations = (query) => (dispatch, getState) => {
if (!query) return if (!query) return
debouncedFetchChatConversationAccountSuggestions(query, dispatch, getState) debouncedSearchApprovedChatConversations(query, dispatch, getState)
} }
export const debouncedFetchChatConversationAccountSuggestions = debounce((query, dispatch, getState) => { export const debouncedSearchApprovedChatConversations = debounce((query, dispatch, getState) => {
if (!query) return if (!query) return
api(getState).get('/api/v1/accounts/search', { api(getState).get('/api/v1/chat_conversation_accounts/_/search', {
params: { params: { q: query },
q: query,
resolve: false,
limit: 4,
},
}).then((response) => { }).then((response) => {
// const next = getLinks(response).refs.find(link => link.rel === 'next') const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts))
// const conversationsAccounts = [].concat.apply([], response.data.map((c) => c.other_accounts)) dispatch(searchApprovedChatConversationsSuccess(response.data))
dispatch(importFetchedAccounts(response.data))
// dispatch(importFetchedAccounts(conversationsAccounts))
// dispatch(importFetchedChatMessages(conversationsChatMessages))
// dispatch(fetchChatConversationsSuccess(response.data, next ? next.uri : null))
dispatch(fetchChatConversationAccountSuggestionsSuccess(response.data))
}).catch((error) => { }).catch((error) => {
// //
}) })
}, 650, { leading: true }) }, 650, { leading: true })
const fetchChatConversationAccountSuggestionsSuccess = (chatConversations) => ({ const searchApprovedChatConversationsSuccess = (chatConversations) => ({
type: CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS, type: CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS,
chatConversations, chatConversations,
}) })

View File

@ -9,6 +9,8 @@ export const CLEAR_CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS = 'CLEAR_CHAT_CONVER
export const SET_CHAT_CONVERSATION_SELECTED = 'SET_CHAT_CONVERSATION_SELECTED' export const SET_CHAT_CONVERSATION_SELECTED = 'SET_CHAT_CONVERSATION_SELECTED'
export const SET_CHAT_CONVERSATION_SEARCH_VALUE = 'SET_CHAT_CONVERSATION_SEARCH_VALUE'
/** /**
* *
*/ */
@ -54,4 +56,14 @@ export const setChatConversationSelected = (chatConversationId) => (dispatch) =>
type: SET_CHAT_CONVERSATION_SELECTED, type: SET_CHAT_CONVERSATION_SELECTED,
chatConversationId, chatConversationId,
}) })
}
/**
*
*/
export const onChangeSearch = (value) => (dispatch) => {
dispatch({
type: SET_CHAT_CONVERSATION_SEARCH_VALUE,
value,
})
} }

View File

@ -62,11 +62,25 @@ class ChatConversationsList extends ImmutablePureComponent {
} }
const mapStateToProps = (state, { source }) => ({ const mapStateToProps = (state, { source }) => {
chatConversationIds: state.getIn(['chat_conversation_lists', source, 'items']), let chatConversationIds
hasMore: !!state.getIn(['chat_conversation_lists', source, 'next']), if (source === 'approved') {
isLoading: state.getIn(['chat_conversation_lists', source, 'isLoading']), const chatSearchValue = state.getIn(['chats', 'searchValue'], '')
}) if (!!chatSearchValue && chatSearchValue.length > 0) {
chatConversationIds = state.getIn(['chat_conversation_lists', 'approved_search', 'items'])
} else {
chatConversationIds = state.getIn(['chat_conversation_lists', source, 'items'])
}
} else {
chatConversationIds = state.getIn(['chat_conversation_lists', source, 'items'])
}
return {
chatConversationIds,
hasMore: !!state.getIn(['chat_conversation_lists', source, 'next']),
isLoading: state.getIn(['chat_conversation_lists', source, 'isLoading']),
}
}
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onFetchChatConversations(source) { onFetchChatConversations(source) {

View File

@ -1,21 +1,18 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { searchApprovedChatConversations } from '../../../actions/chat_conversations'
import { onChangeSearch } from '../../../actions/chats'
import Input from '../../../components/input' import Input from '../../../components/input'
class ChatConversationsSearch extends React.PureComponent { class ChatConversationsSearch extends React.PureComponent {
state = {
value: '',
}
handleOnChange = (value) => { handleOnChange = (value) => {
this.setState({ value })
this.props.onChange(value) this.props.onChange(value)
} }
render() { render() {
const { value } = this.state const { value } = this.props
return ( return (
<div className={[_s.d, _s.h60PX, _s.w100PC, _s.px10, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}> <div className={[_s.d, _s.h60PX, _s.w100PC, _s.px10, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
@ -33,10 +30,20 @@ class ChatConversationsSearch extends React.PureComponent {
} }
const mapStateToProps = (state) => ({
value: state.getIn(['chats', 'searchValue'], ''),
})
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
onChange(value) { onChange(value) {
// dispatch() dispatch(onChangeSearch(value))
dispatch(searchApprovedChatConversations(value))
} }
}) })
export default connect(null, mapDispatchToProps)(ChatConversationsSearch) ChatConversationsSearch.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationsSearch)

View File

@ -45,10 +45,12 @@ class ChatMessagesComposeForm extends React.PureComponent {
} }
onBlur = () => { onBlur = () => {
console.log("onBlur")
this.setState({ focused: false }) this.setState({ focused: false })
} }
onFocus = () => { onFocus = () => {
console.log("onFocus")
this.setState({ focused: true }) this.setState({ focused: true })
} }

View File

@ -67,8 +67,8 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
if (prevProps.chatMessageIds.size === 0 && this.props.chatMessageIds.size > 0 && this.scrollContainerRef) { if (prevProps.chatMessageIds.size === 0 && this.props.chatMessageIds.size > 0 && this.scrollContainerRef) {
this.scrollToBottom() this.scrollToBottom()
this.props.onReadChatConversation(this.props.chatConversationId) this.props.onReadChatConversation(this.props.chatConversationId)
} else if (prevProps.chatMessageIds.size < this.props.chatMessageIds.size && this.scrollContainerRef) { } else if (this.props.chatMessageIds.size - prevProps.chatMessageIds.size === 1) {
// this.setScrollTop(this.scrollContainerRef.scrollHeight) this.scrollToBottom()
} }
} }
@ -121,7 +121,7 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
scrollToBottom = () => { scrollToBottom = () => {
if (this.messagesEnd) { if (this.messagesEnd) {
this.messagesEnd.scrollIntoView({ behavior: 'smooth' }); this.messagesEnd.scrollIntoView()
} }
} }
@ -225,6 +225,11 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
this.containerNode = c this.containerNode = c
} }
setMessagesEnd = (c) => {
this.messagesEnd = c
this.scrollToBottom()
}
setScrollContainerRef = (c) => { setScrollContainerRef = (c) => {
this.scrollContainerRef = c this.scrollContainerRef = c
@ -242,7 +247,6 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
isPartial, isPartial,
hasMore, hasMore,
amITalkingToMyself, amITalkingToMyself,
onScrollToBottom,
onScroll, onScroll,
isXS, isXS,
} = this.props } = this.props
@ -338,11 +342,13 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
</IntersectionObserverArticle> </IntersectionObserverArticle>
)) ))
} }
<div
style={{ float: 'left', clear: 'both' }}
ref={(el) => { this.messagesEnd = el }}
/>
</div> </div>
<div
key='end-message'
style={{ float: 'left', clear: 'both' }}
ref={this.setMessagesEnd}
/>
</div> </div>
</div> </div>
) )

View File

@ -23,6 +23,7 @@ import {
CHAT_CONVERSATIONS_MUTED_EXPAND_SUCCESS, CHAT_CONVERSATIONS_MUTED_EXPAND_SUCCESS,
CHAT_CONVERSATIONS_MUTED_EXPAND_FAIL, CHAT_CONVERSATIONS_MUTED_EXPAND_FAIL,
CHAT_CONVERSATIONS_CREATE_SUCCESS, CHAT_CONVERSATIONS_CREATE_SUCCESS,
CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS,
} from '../actions/chat_conversations' } from '../actions/chat_conversations'
const initialState = ImmutableMap({ const initialState = ImmutableMap({
@ -41,6 +42,9 @@ const initialState = ImmutableMap({
isLoading: false, isLoading: false,
items: ImmutableList(), items: ImmutableList(),
}), }),
approved_search: ImmutableMap({
items: ImmutableList(),
}),
}) })
const normalizeList = (state, source, chatConversations, next) => { const normalizeList = (state, source, chatConversations, next) => {
@ -107,6 +111,9 @@ export default function chat_conversation_lists(state = initialState, action) {
case CHAT_CONVERSATIONS_CREATE_SUCCESS: case CHAT_CONVERSATIONS_CREATE_SUCCESS:
return appendToList(state, 'approved', [action.chatConversation], action.next) return appendToList(state, 'approved', [action.chatConversation], action.next)
case CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS:
return normalizeList(state, 'approved_search', action.chatConversations, null)
default: default:
return state return state
} }

View File

@ -18,6 +18,7 @@ import {
CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS, CHAT_CONVERSATION_REQUEST_APPROVE_SUCCESS,
CHAT_CONVERSATION_MARK_READ_SUCCESS, CHAT_CONVERSATION_MARK_READ_SUCCESS,
SET_CHAT_CONVERSATION_EXPIRATION_SUCCESS, SET_CHAT_CONVERSATION_EXPIRATION_SUCCESS,
CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS,
} from '../actions/chat_conversations' } from '../actions/chat_conversations'
const initialState = ImmutableMap() const initialState = ImmutableMap()
@ -50,6 +51,7 @@ export default function chat_conversations(state = initialState, action) {
case CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS: case CHAT_CONVERSATIONS_APPROVED_EXPAND_SUCCESS:
case CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS: case CHAT_CONVERSATIONS_REQUESTED_FETCH_SUCCESS:
case CHAT_CONVERSATIONS_REQUESTED_EXPAND_SUCCESS: case CHAT_CONVERSATIONS_REQUESTED_EXPAND_SUCCESS:
case CHAT_CONVERSATION_APPROVED_SEARCH_FETCH_SUCCESS:
return importChatConversations(state, action.chatConversations) return importChatConversations(state, action.chatConversations)
case CHAT_MESSAGES_SEND_SUCCESS: case CHAT_MESSAGES_SEND_SUCCESS:
return setLastChatMessage(state, action.chatMessage) return setLastChatMessage(state, action.chatMessage)

View File

@ -8,6 +8,7 @@ import {
CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS_SUCCESS, CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS_SUCCESS,
CLEAR_CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS, CLEAR_CHAT_CONVERSATION_CREATE_SEARCH_ACCOUNTS,
SET_CHAT_CONVERSATION_SELECTED, SET_CHAT_CONVERSATION_SELECTED,
SET_CHAT_CONVERSATION_SEARCH_VALUE,
} from '../actions/chats' } from '../actions/chats'
import { import {
CHAT_CONVERSATION_APPROVED_UNREAD_COUNT_FETCH_SUCCESS, CHAT_CONVERSATION_APPROVED_UNREAD_COUNT_FETCH_SUCCESS,
@ -24,6 +25,7 @@ const initialState = ImmutableMap({
selectedChatConversationId: null, selectedChatConversationId: null,
chatConversationRequestCount: 0, chatConversationRequestCount: 0,
chatsUnreadCount: 0, chatsUnreadCount: 0,
searchValue: '',
}) })
export default function chats(state = initialState, action) { export default function chats(state = initialState, action) {
@ -42,6 +44,8 @@ export default function chats(state = initialState, action) {
const chatConversationUnreadCount = action.chatConversation.get('unread_count') const chatConversationUnreadCount = action.chatConversation.get('unread_count')
const totalUnreadCount = state.get('chatsUnreadCount') const totalUnreadCount = state.get('chatsUnreadCount')
return state.set('chatsUnreadCount', Math.max(totalUnreadCount - chatConversationUnreadCount, 0)) return state.set('chatsUnreadCount', Math.max(totalUnreadCount - chatConversationUnreadCount, 0))
case SET_CHAT_CONVERSATION_SEARCH_VALUE:
return state.set('searchValue', action.value)
default: default:
return state return state
} }

View File

@ -237,6 +237,7 @@ Rails.application.routes.draw do
post :unblock_messenger post :unblock_messenger
post :mute_chat_conversation post :mute_chat_conversation
post :unmute_chat_conversation post :unmute_chat_conversation
get :search
end end
end end