From 137a36b810a50bfa092d68891d19b3e3851b557c Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Thu, 3 Dec 2020 17:13:11 -0500 Subject: [PATCH] Progress with DMs Progress with DMs --- .../admin/custom_emojis_controller.rb | 7 +- .../api/v1/chat_conversation_controller.rb | 2 +- .../approved_conversations_controller.rb | 16 ++- .../chat_conversations/messages_controller.rb | 16 +++ .../api/v1/chat_messages_controller.rb | 28 +++-- .../gabsocial/actions/chat_conversations.js | 16 +++ .../gabsocial/actions/chat_messages.js | 34 +++--- app/javascript/gabsocial/actions/streaming.js | 17 ++- .../gabsocial/components/list_item.js | 1 + .../navigation_bar/default_navigation_bar.js | 10 +- .../components/navigation_bar_button.js | 28 +++-- .../chat_conversation_options_popover.js | 113 ++++++++++++++++++ .../popover/chat_message_delete_popover.js | 54 +++++++++ .../components/popover/popover_root.js | 45 ++++--- .../components/sidebar/default_sidebar.js | 5 +- .../components/sidebar_section_item.js | 12 +- app/javascript/gabsocial/constants.js | 2 + .../gabsocial/containers/gabsocial.js | 2 + .../components/chat_conversations_list.js | 3 - .../chat_conversations_list_item.js | 4 +- .../components/chat_message_compose_form.js | 11 +- .../components/chat_message_header.js | 32 +++-- .../messages/components/chat_message_item.js | 36 ++++-- app/javascript/gabsocial/features/ui/ui.js | 2 +- .../features/ui/util/async_components.js | 2 + .../gabsocial/layouts/messages_layout.js | 2 - app/javascript/gabsocial/pages/home_page.js | 8 ++ .../gabsocial/pages/messages_page.js | 15 ++- .../reducers/chat_conversation_messages.js | 6 +- .../gabsocial/reducers/chat_conversations.js | 13 ++ app/javascript/gabsocial/reducers/chats.js | 6 +- app/javascript/gabsocial/stream.js | 1 - app/javascript/styles/global.css | 5 +- app/lib/inline_renderer.rb | 2 + app/models/account_conversation.rb | 7 -- app/models/chat_block.rb | 4 +- app/models/chat_conversation_account.rb | 22 ++-- app/models/chat_message.rb | 15 +-- app/models/chat_mute.rb | 10 +- .../chat_conversation_account_serializer.rb | 6 +- .../rest/chat_message_serializer.rb | 9 ++ app/views/admin/accounts/index.html.haml | 5 - app/views/admin/custom_emojis/index.html.haml | 18 +-- config/routes.rb | 12 +- config/sidekiq.yml | 3 - ...ead_count_to_chat_conversation_accounts.rb | 10 ++ ..._unread_from_chat_conversation_accounts.rb | 5 + ...3211614_add_expires_at_to_chat_messages.rb | 5 + ...ead_count_to_chat_conversation_accounts.rb | 10 ++ ..._to_chat_conversation_accounts_not_null.rb | 5 + ...on_policy_to_chat_conversation_accounts.rb | 5 + db/schema.rb | 6 +- streaming/index.js | 8 +- 53 files changed, 539 insertions(+), 182 deletions(-) create mode 100644 app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js create mode 100644 app/javascript/gabsocial/components/popover/chat_message_delete_popover.js create mode 100644 db/migrate/20201203211403_add_unread_count_to_chat_conversation_accounts.rb create mode 100644 db/migrate/20201203211419_remove_is_unread_from_chat_conversation_accounts.rb create mode 100644 db/migrate/20201203211614_add_expires_at_to_chat_messages.rb create mode 100644 db/migrate/20201203213059_backfill_add_unread_count_to_chat_conversation_accounts.rb create mode 100644 db/migrate/20201203213108_add_unread_count_to_chat_conversation_accounts_not_null.rb create mode 100644 db/migrate/20201203214600_add_chat_message_expiration_policy_to_chat_conversation_accounts.rb diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 19339bcf..27ce8591 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -70,12 +70,7 @@ module Admin end def filter_params - params.permit( - :local, - :remote, - :by_domain, - :shortcode - ) + params.permit(:shortcode) end end end diff --git a/app/controllers/api/v1/chat_conversation_controller.rb b/app/controllers/api/v1/chat_conversation_controller.rb index 31f7feec..9ada4081 100644 --- a/app/controllers/api/v1/chat_conversation_controller.rb +++ b/app/controllers/api/v1/chat_conversation_controller.rb @@ -26,7 +26,7 @@ class Api::V1::ChatConversationController < Api::BaseController end def mark_chat_conversation_unread - @chat_conversation_account.update!(is_unread: true) + @chat_conversation_account.update!(unread_count: 1) render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer 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 index c606c23b..273ac293 100644 --- a/app/controllers/api/v1/chat_conversations/approved_conversations_controller.rb +++ b/app/controllers/api/v1/chat_conversations/approved_conversations_controller.rb @@ -4,22 +4,30 @@ class Api::V1::ChatConversations::ApprovedConversationsController < Api::BaseCon before_action -> { authorize_if_got_token! :read, :'read:chats' } before_action :require_user! + before_action :set_chat_conversation, only: :create 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 + render json: @chat_conversation, serializer: REST::ChatConversationAccountSerializer + end + + def unread_count + # : todo : make is_unread into unread_count then count + # count = ChatConversationAccount.where(account: current_account, is_hidden: false, is_approved: true, unread_count: true).count + render json: 1 end private + def set_chat_conversation + @chat_conversation = ChatConversationAccount.where(account: current_account).find(params[:id]).first + end + def load_chat_conversations paginated_chat_conversations end diff --git a/app/controllers/api/v1/chat_conversations/messages_controller.rb b/app/controllers/api/v1/chat_conversations/messages_controller.rb index 579e6af8..7f3fa684 100644 --- a/app/controllers/api/v1/chat_conversations/messages_controller.rb +++ b/app/controllers/api/v1/chat_conversations/messages_controller.rb @@ -15,6 +15,22 @@ class Api::V1::ChatConversations::MessagesController < Api::BaseController render json: @chats, each_serializer: REST::ChatMessageSerializer end + def destroy_all + puts "tilly destry all chat" + # : todo : + # check if is pro + # @chat = ChatMessage.where(from_account: current_user.account).find(params[:id]) + + puts "tilly @chat: " + @chat.inspect + + # : 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 diff --git a/app/controllers/api/v1/chat_messages_controller.rb b/app/controllers/api/v1/chat_messages_controller.rb index 8b37b642..22c7dec3 100644 --- a/app/controllers/api/v1/chat_messages_controller.rb +++ b/app/controllers/api/v1/chat_messages_controller.rb @@ -5,25 +5,33 @@ class Api::V1::ChatMessagesController < Api::BaseController before_action -> { doorkeeper_authorize! :write, :'write:chats' } before_action :require_user! - before_action :set_chat_conversation + before_action :set_chat_conversation, only: :create + before_action :set_chat_conversation_recipients, only: :create def create @chat = ChatMessage.create!( from_account: current_account, chat_conversation: @chat_conversation, - text: params[:text] + text: ActionController::Base.helpers.strip_tags(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))) + # check if blocked + + @chat_conversation_recipients.each do |account| + payload = InlineRenderer.render(@chat, account, :chat_message) + Redis.current.publish("chat_messages:#{account.id}", Oj.dump(event: :notification, payload: payload)) + end render json: @chat, serializer: REST::ChatMessageSerializer end def destroy - @chat = ChatMessage.where(account: current_user.account).find(params[:id]) - authorize @chat, :destroy? + puts "tilly destry chat" + + @chat = ChatMessage.where(from_account: current_user.account).find(params[:id]) + + puts "tilly @chat: " + @chat.inspect # : todo : # make sure last_chat_message_id in chat_account_conversation gets set to last @@ -39,6 +47,12 @@ class Api::V1::ChatMessagesController < Api::BaseController @chat_conversation = ChatConversation.find(params[:chat_conversation_id]) end + def set_chat_conversation_recipients + account_conversation = ChatConversationAccount.where(account: current_user.account, chat_conversation: @chat_conversation).first + puts "tilly account_conversation - " + account_conversation.inspect + @chat_conversation_recipients = Account.where(id: account_conversation.participant_account_ids) + end + def chat_params params.permit(:text, :chat_conversation_id) end diff --git a/app/javascript/gabsocial/actions/chat_conversations.js b/app/javascript/gabsocial/actions/chat_conversations.js index 4a594ac3..fc8051e5 100644 --- a/app/javascript/gabsocial/actions/chat_conversations.js +++ b/app/javascript/gabsocial/actions/chat_conversations.js @@ -13,6 +13,8 @@ export const CHAT_CONVERSATIONS_APPROVED_EXPAND_REQUEST = 'CHAT_CONVERSATIONS_AP 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_CONVERSATION_APPROVED_UNREAD_COUNT_FETCH_SUCCESS = 'CHAT_CONVERSATIONS_APPROVED_EXPAND_FAIL' + // export const CHAT_CONVERSATIONS_CREATE_REQUEST = 'CHAT_CONVERSATIONS_CREATE_REQUEST' @@ -437,6 +439,20 @@ export const fetchChatConversationRequestedCount = () => (dispatch, getState) => }) } +/** + * + */ +export const fetchChatConversationUnreadCount = () => (dispatch, getState) => { + if (!me) return + + api(getState).get('/api/v1/chat_conversations/approved_conversations/unread_count').then(response => { + dispatch({ + type: CHAT_CONVERSATION_APPROVED_UNREAD_COUNT_FETCH_SUCCESS, + count: response.data, + }) + }) +} + /** * */ diff --git a/app/javascript/gabsocial/actions/chat_messages.js b/app/javascript/gabsocial/actions/chat_messages.js index 3c89cac0..038e2710 100644 --- a/app/javascript/gabsocial/actions/chat_messages.js +++ b/app/javascript/gabsocial/actions/chat_messages.js @@ -19,7 +19,7 @@ export const sendChatMessage = (text = '', chatConversationId) => (dispatch, get if (!me || !chatConversationId) return if (text.length === 0) return - dispatch(sendMessageRequest()) + dispatch(sendChatMessageRequest(chatConversationId)) api(getState).post('/api/v1/chat_messages', { text, @@ -30,23 +30,23 @@ export const sendChatMessage = (text = '', chatConversationId) => (dispatch, get // }, }).then((response) => { dispatch(importFetchedChatMessages([response.data])) - dispatch(sendMessageSuccess(response.data, chatConversationId)) + dispatch(sendChatMessageSuccess(response.data)) }).catch((error) => { - dispatch(sendMessageFail(error)) + dispatch(sendChatMessageFail(error)) }) } -const sendMessageRequest = () => ({ +const sendChatMessageRequest = (chatConversationId) => ({ type: CHAT_MESSAGES_SEND_REQUEST, -}) - -const sendMessageSuccess = (chatMessage, chatConversationId) => ({ - type: CHAT_MESSAGES_SEND_SUCCESS, - chatMessage, chatConversationId, }) -const sendMessageFail = (error) => ({ +export const sendChatMessageSuccess = (chatMessage) => ({ + type: CHAT_MESSAGES_SEND_SUCCESS, + chatMessage, +}) + +const sendChatMessageFail = (error) => ({ type: CHAT_MESSAGES_SEND_FAIL, error, }) @@ -54,32 +54,32 @@ const sendMessageFail = (error) => ({ /** * */ -const deleteMessage = (chatMessageId) => (dispatch, getState) => { +export const deleteChatMessage = (chatMessageId) => (dispatch, getState) => { if (!me || !chatMessageId) return - dispatch(deleteMessageRequest(chatMessageId)) + dispatch(deleteChatMessageRequest(chatMessageId)) api(getState).delete(`/api/v1/chat_messages/${chatMessageId}`, {}, { // headers: { // 'Idempotency-Key': getState().getIn(['chat_compose', 'idempotencyKey']), // }, }).then((response) => { - deleteMessageSuccess(response) + dispatch(deleteChatMessageSuccess(response.data)) }).catch((error) => { - dispatch(deleteMessageFail(error)) + dispatch(deleteChatMessageFail(error)) }) } -const deleteMessageRequest = (chatMessageId) => ({ +const deleteChatMessageRequest = (chatMessageId) => ({ type: CHAT_MESSAGES_DELETE_REQUEST, chatMessageId, }) -const deleteMessageSuccess = () => ({ +const deleteChatMessageSuccess = () => ({ type: CHAT_MESSAGES_DELETE_SUCCESS, }) -const deleteMessageFail = (error) => ({ +const deleteChatMessageFail = (error) => ({ type: CHAT_MESSAGES_DELETE_FAIL, error, }) \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/streaming.js b/app/javascript/gabsocial/actions/streaming.js index 592b9e51..6ef58d25 100644 --- a/app/javascript/gabsocial/actions/streaming.js +++ b/app/javascript/gabsocial/actions/streaming.js @@ -6,6 +6,7 @@ import { updateTimelineQueue, } from './timelines' import { updateNotificationsQueue } from './notifications' +import { sendChatMessageSuccess } from './chat_messages' import { fetchFilters } from './filters' import { getLocale } from '../locales' import { handleComposeSubmit } from './compose' @@ -76,17 +77,15 @@ export const connectUserStream = () => connectTimelineStream('home', 'user') * */ export const connectChatMessagesStream = (accountId) => { - return connectStream(`chat_messages:${accountId}`, null, (dispatch, getState) => { + return connectStream(`chat_messages`, null, (dispatch, getState) => { return { - onConnect() { - // console.log("chat messages connected") - }, - onDisconnect() { - // console.log("chat messages disconnected") - }, + onConnect() {}, + onDisconnect() {}, onReceive (data) { - // : todo : - console.log("chat messages onReceive:", data) + if (!data['event'] || !data['payload']) return + if (data.event === 'notification') { + dispatch(sendChatMessageSuccess(JSON.parse(data.payload))) + } }, } }) diff --git a/app/javascript/gabsocial/components/list_item.js b/app/javascript/gabsocial/components/list_item.js index 357db825..c95f0cf3 100644 --- a/app/javascript/gabsocial/components/list_item.js +++ b/app/javascript/gabsocial/components/list_item.js @@ -81,6 +81,7 @@ class ListItem extends React.PureComponent { const textContainerClasses = CX({ d: 1, pr5: 1, + w100PC: hideArrow, maxW100PC42PX: !hideArrow || showActive, }) diff --git a/app/javascript/gabsocial/components/navigation_bar/default_navigation_bar.js b/app/javascript/gabsocial/components/navigation_bar/default_navigation_bar.js index 81e49b49..21269845 100644 --- a/app/javascript/gabsocial/components/navigation_bar/default_navigation_bar.js +++ b/app/javascript/gabsocial/components/navigation_bar/default_navigation_bar.js @@ -67,6 +67,8 @@ class DefaultNavigationBar extends ImmutablePureComponent { account, noActions, logoDisabled, + unreadChatsCount, + notificationCount, } = this.props const navigationContainerClasses = CX({ @@ -171,7 +173,8 @@ class DefaultNavigationBar extends ImmutablePureComponent {
- + +
@@ -236,6 +239,7 @@ class DefaultNavigationBar extends ImmutablePureComponent { attrTitle={action.attrTitle} title={action.title} icon={action.icon} + count={action.count} to={action.to || undefined} onClick={action.onClick ? () => action.onClick() : undefined} key={`action-btn-${i}`} @@ -261,6 +265,8 @@ const mapStateToProps = (state) => ({ emailConfirmationResends: state.getIn(['user', 'emailConfirmationResends'], 0), theme: state.getIn(['settings', 'displayOptions', 'theme'], DEFAULT_THEME), logoDisabled: state.getIn(['settings', 'displayOptions', 'logoDisabled'], false), + notificationCount: state.getIn(['notifications', 'unread']), + unreadChatsCount: state.getIn(['chats', 'chatsUnreadCount']), }) const mapDispatchToProps = (dispatch) => ({ @@ -288,6 +294,8 @@ DefaultNavigationBar.propTypes = { tabs: PropTypes.array, title: PropTypes.string, showBackBtn: PropTypes.bool, + notificationCount: PropTypes.number.isRequired, + unreadChatsCount: PropTypes.number.isRequired, onOpenNavSettingsPopover: PropTypes.func.isRequired, onOpenEmailModal: PropTypes.func.isRequired, onResendUserConfirmationEmail: PropTypes.func.isRequired, diff --git a/app/javascript/gabsocial/components/navigation_bar_button.js b/app/javascript/gabsocial/components/navigation_bar_button.js index 58eec948..1ab14300 100644 --- a/app/javascript/gabsocial/components/navigation_bar_button.js +++ b/app/javascript/gabsocial/components/navigation_bar_button.js @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' +import { shortNumberFormat } from '../utils/numbers' import { CX } from '../constants' import Button from './button' import Icon from './icon' @@ -53,17 +54,22 @@ class NavigationBarButton extends React.PureComponent { const countClasses = CX({ d: 1, + text: 1, + textAlignCenter: 1, + minW20PX: 1, + mlAuto: 1, fs12PX: 1, - posAbs: 1, + px5: 1, + mr2: 1, + lineHeight15: 1, + ml5: 1, + cWhite: 1, + rightNeg5PX: 1, top0: 1, - mt15: 1, - right0: 1, - mr5: 1, - h4PX: 1, - w4PX: 1, - cBrand: 1, - bgNavigationBlend: 1, - radiusSmall: 1, + mt5: 1, + bgRed: 1, + posAbs: 1, + circle: 1, }) const iconContainerClasses = CX({ @@ -112,7 +118,9 @@ class NavigationBarButton extends React.PureComponent { } { !title && count > 0 && - + + {shortNumberFormat(count)} + } ) diff --git a/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js b/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js new file mode 100644 index 00000000..1647d56a --- /dev/null +++ b/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js @@ -0,0 +1,113 @@ +import React from 'react' +import PropTypes from 'prop-types' +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import { connect } from 'react-redux' +import { closePopover } from '../../actions/popover' +import { openModal } from '../../actions/modal' +import { MODAL_PRO_UPGRADE } from '../../constants' +import { me } from '../../initial_state' +import { makeGetChatConversation } from '../../selectors' +import { changeSetting, saveSettings } from '../../actions/settings' +import PopoverLayout from './popover_layout' +import List from '../list' + +class ChatConversationOptionsPopover extends ImmutablePureComponent { + + handleOnBlock = () => { + // + } + + handleOnHide = () => { + + } + + handleOnMute = () => { + + } + + handleOnPurge = () => { + if (!this.props.isPro) { + this.props.openProUpgradeModal() + } else { + // + } + } + + handleOnClosePopover = () => { + this.props.onClosePopover() + } + + render() { + const { + intl, + isXS, + } = this.props + + const items = [ + { + hideArrow: true, + title: 'Block Messenger', + subtitle: 'The messenger will not be able to message you.', + onClick: () => this.handleOnBlock(), + }, + { + hideArrow: true, + title: 'Mute Messenger', + subtitle: 'You will not be notified of new messsages', + onClick: () => this.handleOnMute(), + }, + { + hideArrow: true, + title: 'Hide Conversation', + subtitle: 'Hide until next message', + onClick: () => this.handleOnHide(), + }, + {}, + { + hideArrow: true, + title: 'Purge messages', + subtitle: 'Remove all of your messages in this conversation', + onClick: () => this.handleOnPurge(), + }, + ] + + return ( + + + + ) + } +} + +const mapStateToProps = (state, { chatConversationId }) => ({ + isPro: state.getIn(['accounts', me, 'is_pro']), + chatConversation: makeGetChatConversation()(state, { id: chatConversationId }), +}) + +const mapDispatchToProps = (dispatch) => ({ + openProUpgradeModal() { + dispatch(openModal(MODAL_PRO_UPGRADE)) + }, + onSetCommentSortingSetting(type) { + dispatch(closePopover()) + }, + onClosePopover: () => dispatch(closePopover()), +}) + +ChatConversationOptionsPopover.propTypes = { + isXS: PropTypes.bool, + isPro: PropTypes.bool.isRequired, + chatConversation: ImmutablePropTypes.map, + onClosePopover: PropTypes.func.isRequired, +} + +export default connect(mapStateToProps, mapDispatchToProps)(ChatConversationOptionsPopover) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/popover/chat_message_delete_popover.js b/app/javascript/gabsocial/components/popover/chat_message_delete_popover.js new file mode 100644 index 00000000..b0922630 --- /dev/null +++ b/app/javascript/gabsocial/components/popover/chat_message_delete_popover.js @@ -0,0 +1,54 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { closePopover } from '../../actions/popover' +import { deleteChatMessage } from '../../actions/chat_messages' +import PopoverLayout from './popover_layout' +import Button from '../button' +import Text from '../text' + +class ChatMessageDeletePopover extends React.PureComponent { + + handleOnClick = () => { + this.props.onDeleteChatMessage(this.props.chatMessageId) + } + + handleOnClosePopover = () => { + this.props.onClosePopover() + } + + render() { + const { isXS } = this.props + + return ( + + + + ) + } +} + +const mapDispatchToProps = (dispatch) => ({ + onDeleteChatMessage(chatMessageId) { + dispatch(deleteChatMessage(chatMessageId)) + }, +}) + +ChatMessageDeletePopover.propTypes = { + isXS: PropTypes.bool, + chatMessageId: PropTypes.string.isRequired, + onDeleteChatMessage: PropTypes.func.isRequired, +} + +export default connect(null, mapDispatchToProps)(ChatMessageDeletePopover) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/popover/popover_root.js b/app/javascript/gabsocial/components/popover/popover_root.js index 36d22c75..59d4f201 100644 --- a/app/javascript/gabsocial/components/popover/popover_root.js +++ b/app/javascript/gabsocial/components/popover/popover_root.js @@ -1,5 +1,7 @@ import { BREAKPOINT_EXTRA_SMALL, + POPOVER_CHAT_CONVERSATION_OPTIONS, + POPOVER_CHAT_MESSAGE_DELETE, POPOVER_COMMENT_SORTING_OPTIONS, POPOVER_DATE_PICKER, POPOVER_EMOJI_PICKER, @@ -20,6 +22,8 @@ import { POPOVER_VIDEO_STATS, } from '../../constants' import { + ChatConversationOptionsPopover, + ChatMessageDeletePopover, CommentSortingOptionsPopover, DatePickerPopover, EmojiPickerPopover, @@ -53,25 +57,28 @@ import LoadingPopover from './loading_popover' const initialState = getWindowDimension() -const POPOVER_COMPONENTS = {} -POPOVER_COMPONENTS[POPOVER_COMMENT_SORTING_OPTIONS] = CommentSortingOptionsPopover -POPOVER_COMPONENTS[POPOVER_DATE_PICKER] = DatePickerPopover -POPOVER_COMPONENTS[POPOVER_EMOJI_PICKER] = EmojiPickerPopover -POPOVER_COMPONENTS[POPOVER_GROUP_LIST_SORT_OPTIONS] = GroupListSortOptionsPopover -POPOVER_COMPONENTS[POPOVER_GROUP_MEMBER_OPTIONS] = GroupMemberOptionsPopover -POPOVER_COMPONENTS[POPOVER_GROUP_OPTIONS] = GroupOptionsPopover -POPOVER_COMPONENTS[POPOVER_GROUP_TIMELINE_SORT_OPTIONS] = GroupTimelineSortOptionsPopover -POPOVER_COMPONENTS[POPOVER_GROUP_TIMELINE_SORT_TOP_OPTIONS] = GroupTimelineSortTopOptionsPopover -POPOVER_COMPONENTS[POPOVER_NAV_SETTINGS] = NavSettingsPopover -POPOVER_COMPONENTS[POPOVER_PROFILE_OPTIONS] = ProfileOptionsPopover -POPOVER_COMPONENTS[POPOVER_SIDEBAR_MORE] = SidebarMorePopover -POPOVER_COMPONENTS[POPOVER_STATUS_OPTIONS] = StatusOptionsPopover -POPOVER_COMPONENTS[POPOVER_STATUS_EXPIRATION_OPTIONS] = StatusExpirationOptionsPopover -POPOVER_COMPONENTS[POPOVER_STATUS_SHARE] = StatusSharePopover -POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover -POPOVER_COMPONENTS[POPOVER_TIMELINE_INJECTION_OPTIONS] = TimelineInjectionOptionsPopover -POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover -POPOVER_COMPONENTS[POPOVER_VIDEO_STATS] = VideoStatsPopover +const POPOVER_COMPONENTS = { + [POPOVER_CHAT_CONVERSATION_OPTIONS]: ChatConversationOptionsPopover, + [POPOVER_CHAT_MESSAGE_DELETE]: ChatMessageDeletePopover, + [POPOVER_COMMENT_SORTING_OPTIONS]: CommentSortingOptionsPopover, + [POPOVER_DATE_PICKER]: DatePickerPopover, + [POPOVER_EMOJI_PICKER]: EmojiPickerPopover, + [POPOVER_GROUP_LIST_SORT_OPTIONS]: GroupListSortOptionsPopover, + [POPOVER_GROUP_MEMBER_OPTIONS]: GroupMemberOptionsPopover, + [POPOVER_GROUP_OPTIONS]: GroupOptionsPopover, + [POPOVER_GROUP_TIMELINE_SORT_OPTIONS]: GroupTimelineSortOptionsPopover, + [POPOVER_GROUP_TIMELINE_SORT_TOP_OPTIONS]: GroupTimelineSortTopOptionsPopover, + [POPOVER_NAV_SETTINGS]: NavSettingsPopover, + [POPOVER_PROFILE_OPTIONS]: ProfileOptionsPopover, + [POPOVER_SIDEBAR_MORE]: SidebarMorePopover, + [POPOVER_STATUS_OPTIONS]: StatusOptionsPopover, + [POPOVER_STATUS_EXPIRATION_OPTIONS]: StatusExpirationOptionsPopover, + [POPOVER_STATUS_SHARE]: StatusSharePopover, + [POPOVER_STATUS_VISIBILITY]: StatusVisibilityPopover, + [POPOVER_TIMELINE_INJECTION_OPTIONS]: TimelineInjectionOptionsPopover, + [POPOVER_USER_INFO]: UserInfoPopover, + [POPOVER_VIDEO_STATS]: VideoStatsPopover, +} class PopoverRoot extends React.PureComponent { diff --git a/app/javascript/gabsocial/components/sidebar/default_sidebar.js b/app/javascript/gabsocial/components/sidebar/default_sidebar.js index 1098893e..21883c31 100644 --- a/app/javascript/gabsocial/components/sidebar/default_sidebar.js +++ b/app/javascript/gabsocial/components/sidebar/default_sidebar.js @@ -54,6 +54,7 @@ class DefaultSidebar extends ImmutablePureComponent { title, showBackBtn, shortcuts, + unreadChatsCount, } = this.props const { hoveringShortcuts } = this.state @@ -82,7 +83,7 @@ class DefaultSidebar extends ImmutablePureComponent { - + @@ -151,6 +152,7 @@ const mapStateToProps = (state) => ({ shortcuts: state.getIn(['shortcuts', 'items']), moreOpen: state.getIn(['popover', 'popoverType']) === 'SIDEBAR_MORE', notificationCount: state.getIn(['notifications', 'unread']), + unreadChatsCount: state.getIn(['chats', 'chatsUnreadCount']), homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']), }) @@ -170,6 +172,7 @@ DefaultSidebar.propTypes = { openSidebarMorePopover: PropTypes.func.isRequired, notificationCount: PropTypes.number.isRequired, homeItemsQueueCount: PropTypes.number.isRequired, + unreadChatsCount: PropTypes.number.isRequired, actions: PropTypes.array, tabs: PropTypes.array, title: PropTypes.string, diff --git a/app/javascript/gabsocial/components/sidebar_section_item.js b/app/javascript/gabsocial/components/sidebar_section_item.js index 90d368b5..49030763 100644 --- a/app/javascript/gabsocial/components/sidebar_section_item.js +++ b/app/javascript/gabsocial/components/sidebar_section_item.js @@ -45,7 +45,7 @@ class SidebarSectionItem extends React.PureComponent { const iconSize = '16px' const currentPathname = noRouter ? '' : this.context.router.route.location.pathname const shouldShowActive = hovering || active || currentPathname === to || currentPathname === href - const isNotifications = to === '/notifications' + const isHighlighted = ['/notifications', '/messages'].indexOf(to) > -1 const containerClasses = CX({ d: 1, @@ -67,16 +67,18 @@ class SidebarSectionItem extends React.PureComponent { const countClasses = CX({ d: 1, text: 1, + textAlignCenter: 1, + minW20PX: 1, mlAuto: 1, fs12PX: 1, px5: 1, mr2: 1, lineHeight15: 1, ml5: 1, - cSecondary: !isNotifications, - cWhite: isNotifications, - bgBrand: isNotifications, - radiusSmall: isNotifications, + cSecondary: !isHighlighted, + cWhite: isHighlighted, + bgRed: isHighlighted, + circle: isHighlighted, }) return ( diff --git a/app/javascript/gabsocial/constants.js b/app/javascript/gabsocial/constants.js index 61c034de..a048b4c7 100644 --- a/app/javascript/gabsocial/constants.js +++ b/app/javascript/gabsocial/constants.js @@ -22,6 +22,8 @@ export const URL_GAB_PRO = 'https://pro.gab.com' export const PLACEHOLDER_MISSING_HEADER_SRC = '/original/missing.png' +export const POPOVER_CHAT_CONVERSATION_OPTIONS = 'CHAT_CONVERSATION_OPTIONS' +export const POPOVER_CHAT_MESSAGE_DELETE = 'CHAT_MESSAGE_DELETE' export const POPOVER_COMMENT_SORTING_OPTIONS = 'COMMENT_SORTING_OPTIONS' export const POPOVER_DATE_PICKER = 'DATE_PICKER' export const POPOVER_EMOJI_PICKER = 'EMOJI_PICKER' diff --git a/app/javascript/gabsocial/containers/gabsocial.js b/app/javascript/gabsocial/containers/gabsocial.js index 615b7b39..977b5d46 100644 --- a/app/javascript/gabsocial/containers/gabsocial.js +++ b/app/javascript/gabsocial/containers/gabsocial.js @@ -11,6 +11,7 @@ import { ScrollContext } from 'react-router-scroll-4' import { IntlProvider, addLocaleData } from 'react-intl' import { fetchCustomEmojis } from '../actions/custom_emojis' import { fetchPromotions } from '../actions/promotions' +import { fetchChatConversationUnreadCount } from '../actions/chat_conversations' import { hydrateStore } from '../actions/store' import { MIN_ACCOUNT_CREATED_AT_ONBOARDING } from '../constants' import { @@ -35,6 +36,7 @@ const hydrateAction = hydrateStore(initialState) store.dispatch(hydrateAction) store.dispatch(fetchCustomEmojis()) store.dispatch(fetchPromotions()) +store.dispatch(fetchChatConversationUnreadCount()) const mapStateToProps = (state) => ({ accountCreatedAt: !!me ? state.getIn(['accounts', me, 'created_at']) : undefined, diff --git a/app/javascript/gabsocial/features/messages/components/chat_conversations_list.js b/app/javascript/gabsocial/features/messages/components/chat_conversations_list.js index 50bee43a..25a2116e 100644 --- a/app/javascript/gabsocial/features/messages/components/chat_conversations_list.js +++ b/app/javascript/gabsocial/features/messages/components/chat_conversations_list.js @@ -17,7 +17,6 @@ import ScrollableList from '../../../components/scrollable_list' class ChatConversationsList extends ImmutablePureComponent { componentDidMount() { - console.log("componentDidMount:", this.props.source) this.props.onFetchChatConversations(this.props.source) } @@ -33,8 +32,6 @@ class ChatConversationsList extends ImmutablePureComponent { chatConversationIds, } = this.props - console.log("---source:", source, chatConversationIds) - return (
= 28 ? `${lastMessageText.substring(0, 28).trim()}...` : lastMessageText + const content = { __html: lastMessageText } const date = !!lastMessage ? lastMessage.get('created_at') : chatConversation.get('created_at') return ( diff --git a/app/javascript/gabsocial/features/messages/components/chat_message_compose_form.js b/app/javascript/gabsocial/features/messages/components/chat_message_compose_form.js index f3e0505d..8c340a4b 100644 --- a/app/javascript/gabsocial/features/messages/components/chat_message_compose_form.js +++ b/app/javascript/gabsocial/features/messages/components/chat_message_compose_form.js @@ -9,6 +9,7 @@ import { sendChatMessage } from '../../../actions/chat_messages' import { CX } from '../../../constants' import Button from '../../../components/button' import Input from '../../../components/input' +import Text from '../../../components/text' class ChatMessagesComposeForm extends React.PureComponent { @@ -90,13 +91,13 @@ class ChatMessagesComposeForm extends React.PureComponent { maxH200PX: 1, borderColorSecondary: 1, border1PX: 1, - radiusSmall: 1, + radiusRounded: 1, py10: 1, }) return ( -
-
+
+