From 8f94ffad9c43949b8fba0afcc991547707c80801 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Wed, 16 Dec 2020 02:39:07 -0500 Subject: [PATCH] Progress --- .../muted_chat_accounts_controller.rb | 60 ------- .../chat_conversation_accounts_controller.rb | 36 ++-- .../api/web/settings_controller.rb | 2 + .../auth/registrations_controller.rb | 2 +- app/javascript/gabsocial/actions/bookmarks.js | 107 ++++++++++-- .../actions/chat_conversation_accounts.js | 161 ++++-------------- app/javascript/gabsocial/actions/chats.js | 2 + app/javascript/gabsocial/components/album.js | 39 +++++ .../components/modal/album_create_modal.js | 0 .../modal/bookmark_collection_create_modal.js | 29 ++++ .../community_timeline_settings_modal.js | 88 ---------- .../modal/deck_column_add_options_modal.js | 161 ++++++++++++++++-- .../modal/hashtag_timeline_settings_modal.js | 88 ---------- .../gabsocial/components/modal/modal_root.js | 12 +- .../navigation_bar/profile_navigation_bar.js | 14 ++ .../components/panel/media_gallery_panel.js | 2 +- .../components/panel/profile_stats_panel.js | 18 +- .../chat_conversation_options_popover.js | 6 + .../popover/chat_message_options_popover.js | 56 +++--- .../compose_post_destination_popover.js | 17 +- .../popover/status_options_popover.js | 1 + .../gabsocial/components/profile_header.js | 134 +++++++-------- .../components/sidebar/deck_sidebar.js | 14 +- .../gabsocial/components/user_stat.js | 24 ++- app/javascript/gabsocial/constants.js | 4 +- .../gabsocial/features/account_albums.js | 150 ++++++++++++++++ .../gabsocial/features/album_create.js | 0 .../features/bookmark_collection_create.js | 73 ++++++++ .../features/bookmark_collections.js | 49 +++++- .../gabsocial/features/bookmarked_statuses.js | 19 +-- .../features/chat_conversation_create.js | 2 - .../chat_conversation_muted_accounts.js | 87 ---------- .../components/compose_destination_header.js | 43 ++--- .../components/compose_extra_button_list.js | 5 +- .../compose/components/compose_form.js | 2 +- app/javascript/gabsocial/features/deck.js | 32 +++- .../gabsocial/features/list_timeline.js | 4 - .../components/chat_message_compose_form.js | 2 +- .../components/chat_settings_sidebar.js | 4 - app/javascript/gabsocial/features/ui/ui.js | 12 +- .../features/ui/util/async_components.js | 7 +- .../gabsocial/layouts/deck_layout.js | 2 +- .../gabsocial/layouts/profile_layout.js | 2 +- .../gabsocial/pages/community_page.js | 15 +- .../gabsocial/pages/hashtag_page.js | 22 +-- .../gabsocial/reducers/relationships.js | 38 +++-- app/javascript/gabsocial/reducers/settings.js | 18 +- app/javascript/gabsocial/reducers/toasts.js | 7 +- .../gabsocial/reducers/user_lists.js | 18 -- app/javascript/gabsocial/selectors/index.js | 18 +- app/javascript/styles/global.css | 1 + app/models/chat_conversation_account.rb | 1 + app/models/chat_mute.rb | 29 ---- app/models/link_block.rb | 3 + .../account_relationships_presenter.rb | 3 + .../chat_conversation_account_serializer.rb | 10 +- app/services/mute_chat_messenger_service.rb | 9 - app/services/post_chat_message_service.rb | 17 +- app/services/post_status_service.rb | 2 + app/services/unmute_chat_messenger_service.rb | 8 - config/routes.rb | 12 +- ..._is_muted_to_chat_conversation_accounts.rb | 9 + .../20201216051551_remove_chat_mutes.rb | 5 + db/schema.rb | 11 +- 64 files changed, 958 insertions(+), 870 deletions(-) delete mode 100644 app/controllers/api/v1/chat_conversation_accounts/muted_chat_accounts_controller.rb create mode 100644 app/javascript/gabsocial/components/modal/album_create_modal.js create mode 100644 app/javascript/gabsocial/components/modal/bookmark_collection_create_modal.js delete mode 100644 app/javascript/gabsocial/components/modal/community_timeline_settings_modal.js delete mode 100644 app/javascript/gabsocial/components/modal/hashtag_timeline_settings_modal.js create mode 100644 app/javascript/gabsocial/features/account_albums.js create mode 100644 app/javascript/gabsocial/features/album_create.js create mode 100644 app/javascript/gabsocial/features/bookmark_collection_create.js delete mode 100644 app/javascript/gabsocial/features/chat_conversation_muted_accounts.js delete mode 100644 app/models/chat_mute.rb delete mode 100644 app/services/mute_chat_messenger_service.rb delete mode 100644 app/services/unmute_chat_messenger_service.rb create mode 100644 db/migrate/20201216045306_add_is_muted_to_chat_conversation_accounts.rb create mode 100644 db/migrate/20201216051551_remove_chat_mutes.rb diff --git a/app/controllers/api/v1/chat_conversation_accounts/muted_chat_accounts_controller.rb b/app/controllers/api/v1/chat_conversation_accounts/muted_chat_accounts_controller.rb deleted file mode 100644 index 72f5f645..00000000 --- a/app/controllers/api/v1/chat_conversation_accounts/muted_chat_accounts_controller.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::ChatConversationAccounts::MutedChatAccountsController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:mutes' } - before_action :require_user! - after_action :insert_pagination_headers - - def show - @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_chat_accounts_url pagination_params(max_id: pagination_max_id) - end - end - - def prev_path - unless paginated_mutes.empty? - api_v1_chat_conversation_accounts_muted_chat_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/chat_conversation_accounts_controller.rb b/app/controllers/api/v1/chat_conversation_accounts_controller.rb index 1319f083..e1bd8e22 100644 --- a/app/controllers/api/v1/chat_conversation_accounts_controller.rb +++ b/app/controllers/api/v1/chat_conversation_accounts_controller.rb @@ -7,37 +7,31 @@ class Api::V1::ChatConversationAccountsController < Api::BaseController before_action :require_user! before_action :set_account - def is_messenger_blocked - # - end - - def is_messenger_muted - # - end - def block_messenger BlockMessengerService.new.call(current_user.account, @account) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end - def mute_messenger - MuteMessengerService.new.call(current_user.account, @account) - render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships - end - def unblock_messenger UnblockMessengerService.new.call(current_user.account, @account) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end - def unmute_messenger - UnmuteMessegerService.new.call(current_user.account, @account) - render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships + def mute_chat_conversation + @chat_conversation_account.is_muted = true + @chat_conversation_account.save! + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + end + + def unmute_chat_conversation + @chat_conversation_account.is_muted = false + @chat_conversation_account.save! + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer end def set_expiration_policy if current_user.account.is_pro - # + # : todo : render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer else render json: { error: 'You need to be a GabPRO member to access this' }, status: 422 @@ -50,6 +44,14 @@ class Api::V1::ChatConversationAccountsController < Api::BaseController @account = Account.find(params[: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 + def check_account_suspension gone if @account.suspended? end diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb index 21a28273..77128386 100644 --- a/app/controllers/api/web/settings_controller.rb +++ b/app/controllers/api/web/settings_controller.rb @@ -7,6 +7,8 @@ class Api::Web::SettingsController < Api::Web::BaseController setting.data = params[:data] setting.save! + # todo validate all data objects + render_empty_success end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index ccd9b473..6f1d41b1 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -11,7 +11,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :set_cache_headers, only: [:edit, :update] def new - super(&:build_invite_request) + super end def create diff --git a/app/javascript/gabsocial/actions/bookmarks.js b/app/javascript/gabsocial/actions/bookmarks.js index 5f77c9e4..e04dd817 100644 --- a/app/javascript/gabsocial/actions/bookmarks.js +++ b/app/javascript/gabsocial/actions/bookmarks.js @@ -24,79 +24,92 @@ export const BOOKMARK_COLLECTIONS_REMOVE_REQUEST = 'BOOKMARK_COLLECTIONS_REMOVE_ export const BOOKMARK_COLLECTIONS_REMOVE_SUCCESS = 'BOOKMARK_COLLECTIONS_REMOVE_SUCCESS' export const BOOKMARK_COLLECTIONS_REMOVE_FAIL = 'BOOKMARK_COLLECTIONS_REMOVE_FAIL' +// + +export const UPDATE_BOOKMARK_COLLECTION_FAIL = 'UPDATE_BOOKMARK_COLLECTION_FAIL' +export const UPDATE_BOOKMARK_COLLECTION_REQUEST = 'UPDATE_BOOKMARK_COLLECTION_REQUEST' +export const UPDATE_BOOKMARK_COLLECTION_SUCCESS = 'UPDATE_BOOKMARK_COLLECTION_SUCCESS' + +export const UPDATE_BOOKMARK_COLLECTION_STATUS_FAIL = 'UPDATE_BOOKMARK_COLLECTION_STATUS_FAIL' +export const UPDATE_BOOKMARK_COLLECTION_STATUS_REQUEST = 'UPDATE_BOOKMARK_COLLECTION_STATUS_REQUEST' +export const UPDATE_BOOKMARK_COLLECTION_STATUS_SUCCESS = 'UPDATE_BOOKMARK_COLLECTION_STATUS_SUCCESS' + /** * */ -export const fetchBookmarkedStatuses = () => (dispatch, getState) => { +export const fetchBookmarkedStatuses = (bookmarkCollectionId) => (dispatch, getState) => { if (!me) return if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { return } - dispatch(fetchBookmarkedStatusesRequest()) + dispatch(fetchBookmarkedStatusesRequest(bookmarkCollectionId)) api(getState).get('/api/v1/bookmarks').then((response) => { const next = getLinks(response).refs.find(link => link.rel === 'next') dispatch(importFetchedStatuses(response.data)) - dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null)) + dispatch(fetchBookmarkedStatusesSuccess(response.data, bookmarkCollectionId, next ? next.uri : null)) }).catch((error) => { - dispatch(fetchBookmarkedStatusesFail(error)) + dispatch(fetchBookmarkedStatusesFail(bookmarkCollectionId, error)) }) } -const fetchBookmarkedStatusesRequest = () => ({ +const fetchBookmarkedStatusesRequest = (bookmarkCollectionId) => ({ type: BOOKMARKED_STATUSES_FETCH_REQUEST, }) -const fetchBookmarkedStatusesSuccess = (statuses, next) => ({ +const fetchBookmarkedStatusesSuccess = (statuses, bookmarkCollectionId, next) => ({ type: BOOKMARKED_STATUSES_FETCH_SUCCESS, + bookmarkCollectionId, statuses, next, }) -const fetchBookmarkedStatusesFail = (error) => ({ +const fetchBookmarkedStatusesFail = (bookmarkCollectionId, error) => ({ type: BOOKMARKED_STATUSES_FETCH_FAIL, showToast: true, + bookmarkCollectionId, error, }) /** * */ -export const expandBookmarkedStatuses = () => (dispatch, getState) => { +export const expandBookmarkedStatuses = (bookmarkCollectionId) => (dispatch, getState) => { if (!me) return - const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null) + const url = getState().getIn(['status_lists', 'bookmarks', bookmarkCollectionId, 'next'], null) - if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { + if (url === null || getState().getIn(['status_lists', 'bookmarks', bookmarkCollectionId, 'isLoading'])) { return } - dispatch(expandBookmarkedStatusesRequest()) + dispatch(expandBookmarkedStatusesRequest(bookmarkCollectionId)) api(getState).get(url).then((response) => { const next = getLinks(response).refs.find(link => link.rel === 'next') dispatch(importFetchedStatuses(response.data)) - dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null)) + dispatch(expandBookmarkedStatusesSuccess(response.data, bookmarkCollectionId, next ? next.uri : null)) }).catch((error) => { - dispatch(expandBookmarkedStatusesFail(error)) + dispatch(expandBookmarkedStatusesFail(bookmarkCollectionId, error)) }) } -const expandBookmarkedStatusesRequest = () => ({ +const expandBookmarkedStatusesRequest = (bookmarkCollectionId) => ({ type: BOOKMARKED_STATUSES_EXPAND_REQUEST, }) -const expandBookmarkedStatusesSuccess = (statuses, next) => ({ +const expandBookmarkedStatusesSuccess = (statuses, bookmarkCollectionId, next) => ({ type: BOOKMARKED_STATUSES_EXPAND_SUCCESS, statuses, next, }) -const expandBookmarkedStatusesFail = (error) => ({ +const expandBookmarkedStatusesFail = (bookmarkCollectionId, error) => ({ type: BOOKMARKED_STATUSES_EXPAND_FAIL, showToast: true, + bookmarkCollectionId, error, }) @@ -190,4 +203,64 @@ const removeBookmarkCollectionFail = (error) => ({ type: BOOKMARK_COLLECTIONS_CREATE_FAIL, showToast: true, error, -}) \ No newline at end of file +}) + +/** + * + */ +export const updateBookmarkCollection = (bookmarkCollectionId, title) => (dispatch, getState) => { + if (!me || !statusId) return + + dispatch(updateBookmarkCollectionRequest()) + + api(getState).post('/api/v1/bookmark_collections', { title }).then((response) => { + dispatch(updateBookmarkCollectionSuccess(response.data)) + }).catch((error) => { + dispatch(updateBookmarkCollectionFail(error)) + }) +} + +const updateBookmarkCollectionRequest = () => ({ + type: UPDATE_BOOKMARK_COLLECTION_REQUEST, +}) + +const updateBookmarkCollectionSuccess = (bookmarkCollection) => ({ + type: UPDATE_BOOKMARK_COLLECTION_SUCCESS, + bookmarkCollection, +}) + +const updateBookmarkCollectionFail = (error) => ({ + type: UPDATE_BOOKMARK_COLLECTION_FAIL, + showToast: true, + error, +}) + +/** + * + */ +export const updateBookmarkCollectionStatus = (statusId, bookmarkCollectionId) => (dispatch, getState) => { + if (!me || !statusId) return + + dispatch(updateBookmarkCollectionStatusRequest()) + + api(getState).post('/api/v1/bookmark_collections', { title }).then((response) => { + dispatch(updateBookmarkCollectionStatusSuccess(response.data)) + }).catch((error) => { + dispatch(updateBookmarkCollectionStatusFail(error)) + }) +} + +const updateBookmarkCollectionStatusRequest = () => ({ + type: UPDATE_BOOKMARK_COLLECTION_STATUS_REQUEST, +}) + +const updateBookmarkCollectionStatusSuccess = (bookmarkCollection) => ({ + type: UPDATE_BOOKMARK_COLLECTION_STATUS_SUCCESS, + bookmarkCollection, +}) + +const updateBookmarkCollectionStatusFail = (error) => ({ + type: UPDATE_BOOKMARK_COLLECTION_STATUS_FAIL, + showToast: true, + error, +}) diff --git a/app/javascript/gabsocial/actions/chat_conversation_accounts.js b/app/javascript/gabsocial/actions/chat_conversation_accounts.js index 778fbd3f..e895a19d 100644 --- a/app/javascript/gabsocial/actions/chat_conversation_accounts.js +++ b/app/javascript/gabsocial/actions/chat_conversation_accounts.js @@ -24,23 +24,13 @@ export const IS_CHAT_MESSENGER_BLOCKED_SUCCESS = 'IS_CHAT_MESSENGER_BLOCKED_SUCC // -export const CHAT_MESSENGER_MUTES_FETCH_REQUEST = 'CHAT_MESSENGER_MUTES_FETCH_REQUEST' -export const CHAT_MESSENGER_MUTES_FETCH_SUCCESS = 'CHAT_MESSENGER_MUTES_FETCH_SUCCESS' -export const CHAT_MESSENGER_MUTES_FETCH_FAIL = 'CHAT_MESSENGER_MUTES_FETCH_FAIL' +export const MUTE_CHAT_CONVERSATION_REQUEST = 'MUTE_CHAT_CONVERSATION_REQUEST' +export const MUTE_CHAT_CONVERSATION_SUCCESS = 'MUTE_CHAT_CONVERSATION_SUCCESS' +export const MUTE_CHAT_CONVERSATION_FAIL = 'MUTE_CHAT_CONVERSATION_FAIL' -export const CHAT_MESSENGER_MUTES_EXPAND_REQUEST = 'CHAT_MESSENGER_MUTES_EXPAND_REQUEST' -export const CHAT_MESSENGER_MUTES_EXPAND_SUCCESS = 'CHAT_MESSENGER_MUTES_EXPAND_SUCCESS' -export const CHAT_MESSENGER_MUTES_EXPAND_FAIL = 'CHAT_MESSENGER_MUTES_EXPAND_FAIL' - -export const MUTE_CHAT_MESSAGER_REQUEST = 'MUTE_CHAT_MESSAGER_REQUEST' -export const MUTE_CHAT_MESSAGER_SUCCESS = 'MUTE_CHAT_MESSAGER_SUCCESS' -export const MUTE_CHAT_MESSAGER_FAIL = 'MUTE_CHAT_MESSAGER_FAIL' - -export const UNMUTE_CHAT_MESSAGER_REQUEST = 'UNMUTE_CHAT_MESSAGER_REQUEST' -export const UNMUTE_CHAT_MESSAGER_SUCCESS = 'UNMUTE_CHAT_MESSAGER_SUCCESS' -export const UNMUTE_CHAT_MESSAGER_FAIL = 'UNMUTE_CHAT_MESSAGER_FAIL' - -export const IS_CHAT_MESSENGER_MUTED_SUCCESS = 'IS_CHAT_MESSENGER_MUTED_SUCCESS' +export const UNMUTE_CHAT_CONVERSATION_REQUEST = 'UNMUTE_CHAT_CONVERSATION_REQUEST' +export const UNMUTE_CHAT_CONVERSATION_SUCCESS = 'UNMUTE_CHAT_CONVERSATION_SUCCESS' +export const UNMUTE_CHAT_CONVERSATION_FAIL = 'UNMUTE_CHAT_CONVERSATION_FAIL' /** * @@ -96,12 +86,12 @@ const unblockChatMessengerRequest = (accountId) => ({ }) const unblockChatMessengerSuccess = () => ({ - type: UNBLOCK_CHAT_MESSAGER_REQUEST, + type: UNBLOCK_CHAT_MESSAGER_SUCCESS, showToast: true, }) const unblockChatMessengerFail = (accountId, error) => ({ - type: UNBLOCK_CHAT_MESSAGER_REQUEST, + type: UNBLOCK_CHAT_MESSAGER_FAIL, showToast: true, accountId, error, @@ -195,147 +185,64 @@ export const expandChatMessengerBlocksFail = (error) => ({ /** * */ -export const muteChatMessenger = (accountId) => (dispatch, getState) => { - if (!me || !accountId) return +export const muteChatConversation = (chatConversationId) => (dispatch, getState) => { + if (!me || !chatConversationId) return - dispatch(muteChatMessengerRequest(accountId)) + dispatch(muteChatConversationRequest(chatConversationId)) - api(getState).post(`/api/v1/chat_conversation_accounts/${accountId}/mute_messenger`).then((response) => { - dispatch(muteChatMessengerSuccess()) + api(getState).post(`/api/v1/chat_conversation_accounts/${chatConversationId}/mute_chat_conversation`).then((response) => { + dispatch(muteChatConversationSuccess(response.data)) }).catch((error) => { - dispatch(muteChatMessengerFail(accountId, error)) + dispatch(muteChatMessengerFail(error)) }) } -const muteChatMessengerRequest = (accountId) => ({ - type: MUTE_CHAT_MESSAGER_REQUEST, +const muteChatConversationRequest = (accountId) => ({ + type: MUTE_CHAT_CONVERSATION_REQUEST, accountId, }) -const muteChatMessengerSuccess = () => ({ - type: MUTE_CHAT_MESSAGER_SUCCESS, +const muteChatConversationSuccess = (chatConversation) => ({ + type: MUTE_CHAT_CONVERSATION_SUCCESS, + chatConversation, showToast: true, }) -const muteChatMessengerFail = (accountId, error) => ({ - type: MUTE_CHAT_MESSAGER_FAIL, +const muteChatConversationFail = (error) => ({ + type: MUTE_CHAT_CONVERSATION_FAIL, showToast: true, - accountId, error, }) /** * */ -export const unmuteChatMessenger = (accountId) => (dispatch, getState) => { - if (!me || !accountId) return +export const unmuteChatConversation = (chatConversationId) => (dispatch, getState) => { + if (!me || !chatConversationId) return - dispatch(unmuteChatMessengerRequest(accountId)) + dispatch(unmuteChatConversationRequest(chatConversationId)) - api(getState).post(`/api/v1/chat_conversation_accounts/${accountId}/unmute_messenger`).then((response) => { - dispatch(unmuteChatMessengerSuccess()) + api(getState).post(`/api/v1/chat_conversation_accounts/${chatConversationId}/unmute_chat_conversation`).then((response) => { + dispatch(unmuteChatConversationSuccess(response.data)) }).catch((error) => { - dispatch(unmuteChatMessengerFail(accountId, error)) + dispatch(unmuteChatConversationFail(error)) }) } -const unmuteChatMessengerRequest = (accountId) => ({ - type: UNMUTE_CHAT_MESSAGER_REQUEST, +const unmuteChatConversationRequest = (accountId) => ({ + type: UNMUTE_CHAT_CONVERSATION_REQUEST, accountId, }) -const unmuteChatMessengerSuccess = () => ({ - type: UNMUTE_CHAT_MESSAGER_REQUEST, +const unmuteChatConversationSuccess = (chatConversation) => ({ + type: UNMUTE_CHAT_CONVERSATION_SUCCESS, + chatConversation, showToast: true, }) -const unmuteChatMessengerFail = (accountId, error) => ({ - type: UNMUTE_CHAT_MESSAGER_REQUEST, +const unmuteChatConversationFail = (accountId, error) => ({ + type: UNMUTE_CHAT_CONVERSATION_FAIL, showToast: true, accountId, error, }) - -/** - * @description Check if a chat messenger is muted by the current user account. - * @param {String} accountId - */ -export const isChatMessengerMuted = (accountId) => (dispatch, getState) => { - if (!me || !accountId) return - - api(getState).post(`/api/v1/chat_conversation_accounts/${accountId}/is_messenger_muted`).then((response) => { - dispatch(isChatMessengerMutedSuccess(response.data)) - }) -} - -const isChatMessengerMutedSuccess = (data) => ({ - type: IS_CHAT_MESSENGER_MUTED_SUCCESS, - data, -}) - -/** - * - */ -export const fetchChatMessengerMutes = () => (dispatch, getState) => { - if (!me) return - - dispatch(fetchChatMessengerMutesRequest()) - - api(getState).get('/api/v1/chat_conversation_accounts/muted_chat_accounts').then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next') - dispatch(importFetchedAccounts(response.data)) - dispatch(fetchChatMessengerMutesSuccess(response.data, next ? next.uri : null)) - }).catch(error => dispatch(fetchChatMessengerMutesFail(error))) -} - -export const fetchChatMessengerMutesRequest = () => ({ - type: CHAT_MESSENGER_MUTES_FETCH_REQUEST, -}) - -export const fetchChatMessengerMutesSuccess = (accounts, next) => ({ - type: CHAT_MESSENGER_MUTES_FETCH_SUCCESS, - accounts, - next, -}) - -export const fetchChatMessengerMutesFail = (error) => ({ - type: CHAT_MESSENGER_MUTES_FETCH_FAIL, - showToast: true, - error, -}) - -/** - * - */ -export const expandChatMessengerMutes = () => (dispatch, getState) => { - if (!me) return - - const url = getState().getIn(['user_lists', 'chat_mutes', me, 'next']) - const isLoading = getState().getIn(['user_lists', 'chat_mutes', me, 'isLoading']) - - if (url === null || isLoading) return - - dispatch(expandChatMessengerMutesRequest()) - - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next') - dispatch(importFetchedAccounts(response.data)) - dispatch(expandChatMessengerMutesSuccess(response.data, next ? next.uri : null)) - }).catch(error => dispatch(expandChatMessengerMutesFail(error))) -} - -export const expandChatMessengerMutesRequest = () => ({ - type: CHAT_MESSENGER_MUTES_EXPAND_REQUEST, -}) - -export const expandChatMessengerMutesSuccess = (accounts, next) => ({ - type: CHAT_MESSENGER_MUTES_EXPAND_SUCCESS, - accounts, - next, -}) - -export const expandChatMessengerMutesFail = (error) => ({ - type: CHAT_MESSENGER_MUTES_EXPAND_FAIL, - error, -}) - diff --git a/app/javascript/gabsocial/actions/chats.js b/app/javascript/gabsocial/actions/chats.js index 2aeff5a7..df8ae5cb 100644 --- a/app/javascript/gabsocial/actions/chats.js +++ b/app/javascript/gabsocial/actions/chats.js @@ -11,6 +11,8 @@ export const SET_CHAT_CONVERSATION_SELECTED = 'SET_CHAT_CONVERSATION_SELECTED' * */ export const fetchChatConversationAccountSuggestions = (query) => throttle((dispatch, getState) => { + if (!query) return + api(getState).get('/api/v1/accounts/search', { params: { q: query, diff --git a/app/javascript/gabsocial/components/album.js b/app/javascript/gabsocial/components/album.js index e69de29b..fe0d03ca 100644 --- a/app/javascript/gabsocial/components/album.js +++ b/app/javascript/gabsocial/components/album.js @@ -0,0 +1,39 @@ +import React from 'react' +import PropTypes from 'prop-types' +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import { CX } from '../constants' +import Button from './button' +import Icon from './icon' +import Image from './image' +import Text from './text' + +class Album extends React.PureComponent { + + handleOnClick = (e) => { + // + } + + render() { + const { album } = this.props + + return ( + + ) + } + +} + +Album.propTypes = { + album: ImmutablePropTypes.map, + isAddable: PropTypes.bool, +} + +export default Album \ No newline at end of file diff --git a/app/javascript/gabsocial/components/modal/album_create_modal.js b/app/javascript/gabsocial/components/modal/album_create_modal.js new file mode 100644 index 00000000..e69de29b diff --git a/app/javascript/gabsocial/components/modal/bookmark_collection_create_modal.js b/app/javascript/gabsocial/components/modal/bookmark_collection_create_modal.js new file mode 100644 index 00000000..8dcf18ff --- /dev/null +++ b/app/javascript/gabsocial/components/modal/bookmark_collection_create_modal.js @@ -0,0 +1,29 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { defineMessages, injectIntl } from 'react-intl' +import ModalLayout from './modal_layout' +import BookmarkCollectionCreate from '../../features/bookmark_collection_create' + +class BookmarkCollectionCreateModal extends React.PureComponent { + + render() { + const { onClose } = this.props + + return ( + + + + ) + } + +} + +BookmarkCollectionCreateModal.propTypes = { + onClose: PropTypes.func.isRequired, +} + +export default BookmarkCollectionCreateModal \ No newline at end of file diff --git a/app/javascript/gabsocial/components/modal/community_timeline_settings_modal.js b/app/javascript/gabsocial/components/modal/community_timeline_settings_modal.js deleted file mode 100644 index 99fb9560..00000000 --- a/app/javascript/gabsocial/components/modal/community_timeline_settings_modal.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { defineMessages, injectIntl } from 'react-intl' -import ImmutablePureComponent from 'react-immutable-pure-component' -import ImmutablePropTypes from 'react-immutable-proptypes' -import { changeSetting, saveSettings } from '../../actions/settings' -import ModalLayout from './modal_layout' -import Button from '../button' -import SettingSwitch from '../setting_switch' -import Text from '../text' - -class CommunityTimelineSettingsModal extends ImmutablePureComponent { - - handleSaveAndClose = () => { - this.props.onSave() - } - - render() { - const { - intl, - settings, - onChange, - onClose, - } = this.props - - return ( - - -
- -
- - - -
- ) - } -} - -const messages = defineMessages({ - title: { id: 'community_timeline_settings', defaultMessage: 'Community Feed Settings' }, - saveAndClose: { id: 'saveClose', defaultMessage: 'Save & Close' }, - onlyMedia: { id: 'community.column_settings.media_only', defaultMessage: 'Media Only' }, -}) - -const mapStateToProps = (state) => ({ - settings: state.getIn(['settings', 'community']), -}) - -const mapDispatchToProps = (dispatch, { onClose }) => ({ - onChange(key, checked) { - dispatch(changeSetting(['community', ...key], checked)) - }, - onSave() { - dispatch(saveSettings()) - onClose() - }, -}) - -CommunityTimelineSettingsModal.propTypes = { - intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map.isRequired, - onChange: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, -} - -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(CommunityTimelineSettingsModal)) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/modal/deck_column_add_options_modal.js b/app/javascript/gabsocial/components/modal/deck_column_add_options_modal.js index 7f9826f6..a452152e 100644 --- a/app/javascript/gabsocial/components/modal/deck_column_add_options_modal.js +++ b/app/javascript/gabsocial/components/modal/deck_column_add_options_modal.js @@ -1,36 +1,138 @@ 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 { openModal } from '../../actions/modal' +import { setDeckColumnAtIndex } from '../../actions/deck' +import { getOrderedLists, getListOfGroups } from '../../selectors' +import { fetchLists } from '../../actions/lists' +import { fetchGroupsByTab } from '../../actions/groups' import { MODAL_DECK_COLUMN_ADD } from '../../constants' import Heading from '../heading' import Button from '../button' import Block from '../block' +import Input from '../input' +import List from '../list' -class DeckColumnAddOptionsModal extends React.PureComponent { +class DeckColumnAddOptionsModal extends ImmutablePureComponent { state = { - selectedItem: null, + hashtagValue: '', + usernameValue: '', + } + + componentDidMount() { + switch (this.props.column) { + case 'list': + this.props.onFetchLists() + break + case 'group': + this.props.onFetchMemberGroups() + break + default: + break + } } onClickClose = () => { this.props.onClose() - this.props.dispatch(openModal(MODAL_DECK_COLUMN_ADD)) + this.props.onOpenDeckColumnAddModal() } - handleAdd = () => { - // + handleAdd = (id) => { + this.props.onSetDeckColumn(id) + this.props.onClose() + } + + handleAddHashtag = () => { + this.handleAdd(`hashtag.${this.state.hashtagValue}`) + this.setState({ hashtagValue: '' }) + } + + onChangeHashtagValue = (hashtagValue) => { + this.setState({ hashtagValue }) + } + + onChangeUsernameValue = (usernameValue) => { + this.setState({ usernameValue }) + } + + getContentForColumn = () => { + const { column, lists, groups, accounts } = this.props + const { hashtagValue } = this.state + + if (column === 'hashtag') { + return ( +
+ +
+ ) + } else if (column === 'list') { + const listItems = lists.map((list) => ({ + onClick: () => this.handleAdd(`list.${list.get('id')}`), + title: list.get('title'), + })) + + return ( +
+ +
+ ) + } else if (column === 'group') { + const listItems = groups.map((group) => ({ + onClick: () => this.handleAdd(`group.${group.get('id')}`), + title: group.get('title'), + })) + + return ( +
+ +
+ ) + } else if (column === 'group') { + return ( +
+ +
+ ) + } + } render() { const { column } = this.props - const { selectedItem } = this.state - - // user, hashtag, list, groups + const { hashtagValue } = this.state if (!column) return
const title = `Select a ${column}` + const content = this.getContentForColumn() + return (
@@ -49,16 +151,19 @@ class DeckColumnAddOptionsModal extends React.PureComponent { {title}
- + { + column === 'hashtag' && + + }
- test + {content}
@@ -67,9 +172,33 @@ class DeckColumnAddOptionsModal extends React.PureComponent { } +const mapStateToProps = (state) => ({ + lists: getOrderedLists(state), + groups: getListOfGroups(state, { type: 'member' }), + accounts: [], +}) + +const mapDispatchToProps = (dispatch) => ({ + onFetchLists() { + dispatch(fetchLists()) + }, + onFetchMemberGroups() { + dispatch(fetchGroupsByTab('member')) + }, + onSetDeckColumn(id) { + dispatch(setDeckColumnAtIndex(id)) + }, + onOpenDeckColumnAddModal() { + dispatch(openModal(MODAL_DECK_COLUMN_ADD)) + }, +}) + DeckColumnAddOptionsModal.propTypes = { + groupIds: ImmutablePropTypes.list, onClose: PropTypes.func.isRequired, + onFetchLists: PropTypes.func.isRequired, + onSetDeckColumn: PropTypes.func.isRequired, column: PropTypes.string.isRequired, } -export default connect()(DeckColumnAddOptionsModal) \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(DeckColumnAddOptionsModal) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/modal/hashtag_timeline_settings_modal.js b/app/javascript/gabsocial/components/modal/hashtag_timeline_settings_modal.js deleted file mode 100644 index e37c2c67..00000000 --- a/app/javascript/gabsocial/components/modal/hashtag_timeline_settings_modal.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { defineMessages, injectIntl } from 'react-intl' -import ImmutablePureComponent from 'react-immutable-pure-component' -import ImmutablePropTypes from 'react-immutable-proptypes' -import { changeSetting, saveSettings } from '../../actions/settings' -import ModalLayout from './modal_layout' -import Button from '../button' -import SettingSwitch from '../setting_switch' -import Text from '../text' - -class HashtagTimelineSettingsModal extends ImmutablePureComponent { - - handleSaveAndClose = () => { - this.props.onSave() - } - - render() { - const { intl, settings, onChange, onClose } = this.props - - // : todo : - - return ( - - -
- -
- - - -
- ) - } -} - -const messages = defineMessages({ - title: { id: 'hashtag_timeline_settings', defaultMessage: 'Hashtag Timeline Settings' }, - saveAndClose: { id: 'saveClose', defaultMessage: 'Save & Close' }, - onlyMedia: { id: 'community.column_settings.media_only', defaultMessage: 'Media Only' }, - showInSidebar: { id: 'show_in_sidebar', defaultMessage: 'Show in Sidebar' }, -}) - -const mapStateToProps = (state) => ({ - settings: state.getIn(['settings', 'community']), -}) - -const mapDispatchToProps = (dispatch, { onClose }) => { - return { - onChange(key, checked) { - dispatch(changeSetting(['community', ...key], checked)) - }, - onSave() { - dispatch(saveSettings()) - onClose() - }, - } -} - -HasttagTimelineSettingsModal.propTypes = { - intl: PropTypes.object.isRequired, - settings: ImmutablePropTypes.map.isRequired, - onChange: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, -} - -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(HashtagTimelineSettingsModal)) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/modal/modal_root.js b/app/javascript/gabsocial/components/modal/modal_root.js index 2a3e5354..b44c04d7 100644 --- a/app/javascript/gabsocial/components/modal/modal_root.js +++ b/app/javascript/gabsocial/components/modal/modal_root.js @@ -8,11 +8,12 @@ import ModalBase from './modal_base' import BundleErrorModal from './bundle_error_modal' import LoadingModal from './loading_modal' import { + MODAL_ALBUM_CREATE, MODAL_BLOCK_ACCOUNT, + MODAL_BOOKMARK_COLLECTION_CREATE, MODAL_BOOST, MODAL_CHAT_CONVERSATION_CREATE, MODAL_CHAT_CONVERSATION_DELETE, - MODAL_COMMUNITY_TIMELINE_SETTINGS, MODAL_COMPOSE, MODAL_CONFIRM, MODAL_DECK_COLUMN_ADD, @@ -24,7 +25,6 @@ import { MODAL_GROUP_CREATE, MODAL_GROUP_DELETE, MODAL_GROUP_PASSWORD, - MODAL_HASHTAG_TIMELINE_SETTINGS, MODAL_HOME_TIMELINE_SETTINGS, MODAL_HOTKEYS, MODAL_LIST_ADD_USER, @@ -44,11 +44,12 @@ import { MODAL_VIDEO, } from '../../constants' import { + AlbumCreateModal, BlockAccountModal, + BookmarkCollectionCreateModal, BoostModal, ChatConversationCreateModal, ChatConversationDeleteModal, - CommunityTimelineSettingsModal, ComposeModal, ConfirmationModal, DeckColumnAddModal, @@ -62,7 +63,6 @@ import { GroupMembersModal, GroupPasswordModal, GroupRemovedAccountsModal, - HashtagTimelineSettingsModal, HomeTimelineSettingsModal, HotkeysModal, ListAddUserModal, @@ -83,11 +83,12 @@ import { } from '../../features/ui/util/async_components' const MODAL_COMPONENTS = { + [MODAL_ALBUM_CREATE]: AlbumCreateModal, [MODAL_BLOCK_ACCOUNT]: BlockAccountModal, + [MODAL_BOOKMARK_COLLECTION_CREATE]: BookmarkCollectionCreateModal, [MODAL_BOOST]: BoostModal, [MODAL_CHAT_CONVERSATION_CREATE]: ChatConversationCreateModal, [MODAL_CHAT_CONVERSATION_DELETE]: ChatConversationDeleteModal, - [MODAL_COMMUNITY_TIMELINE_SETTINGS]: CommunityTimelineSettingsModal, [MODAL_COMPOSE]: ComposeModal, [MODAL_CONFIRM]: ConfirmationModal, [MODAL_DECK_COLUMN_ADD]: DeckColumnAddModal, @@ -99,7 +100,6 @@ const MODAL_COMPONENTS = { [MODAL_GROUP_CREATE]: GroupCreateModal, [MODAL_GROUP_DELETE]: GroupDeleteModal, [MODAL_GROUP_PASSWORD]: GroupPasswordModal, - [MODAL_HASHTAG_TIMELINE_SETTINGS]: HashtagTimelineSettingsModal, [MODAL_HOME_TIMELINE_SETTINGS]: HomeTimelineSettingsModal, [MODAL_HOTKEYS]: HotkeysModal, [MODAL_LIST_ADD_USER]: ListAddUserModal, diff --git a/app/javascript/gabsocial/components/navigation_bar/profile_navigation_bar.js b/app/javascript/gabsocial/components/navigation_bar/profile_navigation_bar.js index c9021f1a..6f11db5e 100644 --- a/app/javascript/gabsocial/components/navigation_bar/profile_navigation_bar.js +++ b/app/javascript/gabsocial/components/navigation_bar/profile_navigation_bar.js @@ -1,6 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import BackButton from '../back_button' +import Button from '../button' import Heading from '../heading' class ProfileNavigationBar extends React.PureComponent { @@ -28,6 +29,19 @@ class ProfileNavigationBar extends React.PureComponent { +
+
+ diff --git a/app/javascript/gabsocial/components/panel/media_gallery_panel.js b/app/javascript/gabsocial/components/panel/media_gallery_panel.js index f0ff3655..7f82b544 100644 --- a/app/javascript/gabsocial/components/panel/media_gallery_panel.js +++ b/app/javascript/gabsocial/components/panel/media_gallery_panel.js @@ -61,7 +61,7 @@ class MediaGalleryPanel extends ImmutablePureComponent { noPadding title={intl.formatMessage(messages.title)} headerButtonTitle={!!account ? intl.formatMessage(messages.show_all) : undefined} - headerButtonTo={!!account ? `/${account.get('acct')}/photos` : undefined} + headerButtonTo={!!account ? `/${account.get('acct')}/albums` : undefined} >
{ diff --git a/app/javascript/gabsocial/components/panel/profile_stats_panel.js b/app/javascript/gabsocial/components/panel/profile_stats_panel.js index 896441cb..e2320ba6 100644 --- a/app/javascript/gabsocial/components/panel/profile_stats_panel.js +++ b/app/javascript/gabsocial/components/panel/profile_stats_panel.js @@ -32,35 +32,27 @@ class ProfileStatsPanel extends ImmutablePureComponent { !!account && - { - account.get('id') === me && - - } } diff --git a/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js b/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js index 70fdcf19..df1429f4 100644 --- a/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js +++ b/app/javascript/gabsocial/components/popover/chat_conversation_options_popover.js @@ -53,6 +53,12 @@ class ChatConversationOptionsPopover extends ImmutablePureComponent { subtitle: 'Hide until next message', onClick: () => this.handleOnHide(), }, + { + hideArrow: true, + title: 'Mute Conversation', + subtitle: "Don't get notified of new messages", + onClick: () => this.handleOnHide(), + }, {}, { hideArrow: true, diff --git a/app/javascript/gabsocial/components/popover/chat_message_options_popover.js b/app/javascript/gabsocial/components/popover/chat_message_options_popover.js index 9ce25bef..0b86521d 100644 --- a/app/javascript/gabsocial/components/popover/chat_message_options_popover.js +++ b/app/javascript/gabsocial/components/popover/chat_message_options_popover.js @@ -4,14 +4,11 @@ import { connect } from 'react-redux' import { closePopover } from '../../actions/popover' import { deleteChatMessage } from '../../actions/chat_messages' import { - isChatMessengerBlocked, - isChatMessengerMuted, blockChatMessenger, unblockChatMessenger, - muteChatMessenger, - unmuteChatMessenger, reportChatMessage, } from '../../actions/chat_conversation_accounts' +import { fetchRelationships } from '../../actions/accounts' import { makeGetChatMessage } from '../../selectors' import { me } from '../../initial_state' import PopoverLayout from './popover_layout' @@ -21,6 +18,12 @@ import Text from '../text' class ChatMessageOptionsPopover extends React.PureComponent { + componentDidMount() { + if (!this.props.isMine) { + this.props.onFetchRelationships(this.props.fromAccountId) + } + } + handleOnDelete = () => { this.props.onDeleteChatMessage(this.props.chatMessageId) } @@ -31,17 +34,9 @@ class ChatMessageOptionsPopover extends React.PureComponent { handleOnBlock = () => { if (this.props.isBlocked) { - this.props.unblockChatMessenger(this.props.fromAccountId) + this.props.onUnblock(this.props.fromAccountId) } else { - this.props.blockChatMessenger(this.props.fromAccountId) - } - } - - handleOnMute = () => { - if (this.props.isMuted) { - this.props.unmuteChatMessenger(this.props.fromAccountId) - } else { - this.props.muteChatMessenger(this.props.fromAccountId) + this.props.onBlock(this.props.fromAccountId) } } @@ -53,7 +48,6 @@ class ChatMessageOptionsPopover extends React.PureComponent { const { isXS, isMine, - isMuted, isBlocked, } = this.props @@ -76,12 +70,6 @@ class ChatMessageOptionsPopover extends React.PureComponent { subtitle: isBlocked ? '' : 'The messenger will not be able to message you.', onClick: () => this.handleOnBlock(), }, - { - hideArrow: true, - title: isMuted ? 'Unmute Messenger' : 'Mute Messenger', - subtitle: isMuted ? '' : 'You will not be notified of new messsages', - onClick: () => this.handleOnMute(), - }, ] return ( @@ -96,12 +84,15 @@ class ChatMessageOptionsPopover extends React.PureComponent { } } -const mapStateToProps = (state, { chatMessageId }) => ({ - isMine: state.getIn(['chat_messages', chatMessageId, 'from_account_id']) === me, - fromAccountId: state.getIn(['chat_messages', chatMessageId, 'from_account_id']), - isBlocked: state.getIn(['chat_messages', chatMessageId, 'from_account_id']), - isMuted: state.getIn(['chat_messages', chatMessageId, 'from_account_id']), -}) +const mapStateToProps = (state, { chatMessageId }) => { + const fromAccountId = state.getIn(['chat_messages', chatMessageId, 'from_account_id']) + + return { + fromAccountId, + isMine: fromAccountId === me, + isBlocked: state.getIn(['relationships', fromAccountId, 'chat_blocked_by'], false), + } +} const mapDispatchToProps = (dispatch) => ({ onDeleteChatMessage(chatMessageId) { @@ -114,15 +105,12 @@ const mapDispatchToProps = (dispatch) => ({ onUnblock(accountId) { dispatch(unblockChatMessenger(accountId)) }, - onMute(accountId) { - dispatch(muteChatMessenger(accountId)) - }, - onUnmute(accountId) { - dispatch(unmuteChatMessenger(accountId)) - }, onReportChatMessage(chatMessageId) { dispatch(reportChatMessage(chatMessageId)) }, + onFetchRelationships(accountId) { + // dispatch(fetchRelationships(accountId)) + }, onClosePopover() { dispatch(closePopover()) }, @@ -130,9 +118,9 @@ const mapDispatchToProps = (dispatch) => ({ ChatMessageOptionsPopover.propTypes = { isXS: PropTypes.bool, + isMine: PropTypes.bool, chatMessageId: PropTypes.string.isRequired, isBlocked: PropTypes.bool.isRequired, - isMuted: PropTypes.bool.isRequired, onDeleteChatMessage: PropTypes.func.isRequired, } diff --git a/app/javascript/gabsocial/components/popover/compose_post_destination_popover.js b/app/javascript/gabsocial/components/popover/compose_post_destination_popover.js index 998898ca..586f3d91 100644 --- a/app/javascript/gabsocial/components/popover/compose_post_destination_popover.js +++ b/app/javascript/gabsocial/components/popover/compose_post_destination_popover.js @@ -4,6 +4,7 @@ import { connect } from 'react-redux' import { closePopover } from '../../actions/popover' import PopoverLayout from './popover_layout' import List from '../list' +import Button from '../button' import Text from '../text' class ComposePostDesinationPopover extends React.PureComponent { @@ -38,8 +39,20 @@ class ComposePostDesinationPopover extends React.PureComponent { isXS={isXS} onClose={this.handleOnClosePopover} > - Post to: - +
+ Post to: + +
+
+ +
) } diff --git a/app/javascript/gabsocial/components/popover/status_options_popover.js b/app/javascript/gabsocial/components/popover/status_options_popover.js index 7ce05ba8..0170caf2 100644 --- a/app/javascript/gabsocial/components/popover/status_options_popover.js +++ b/app/javascript/gabsocial/components/popover/status_options_popover.js @@ -78,6 +78,7 @@ class StatusOptionsPopover extends ImmutablePureComponent { handleGroupRemoveAccount = () => { const { status } = this.props + // : todo : check this.props.onGroupRemoveAccount(status.getIn(['group', 'id']), status.getIn(['account', 'id'])) } diff --git a/app/javascript/gabsocial/components/profile_header.js b/app/javascript/gabsocial/components/profile_header.js index 4c7bca18..081d4b6e 100644 --- a/app/javascript/gabsocial/components/profile_header.js +++ b/app/javascript/gabsocial/components/profile_header.js @@ -107,7 +107,7 @@ class ProfileHeader extends ImmutablePureComponent { title: intl.formatMessage(messages.comments), }, { - to: `/${account.get('acct')}/photos`, + to: `/${account.get('acct')}/albums`, title: intl.formatMessage(messages.photos), }, { @@ -119,7 +119,11 @@ class ProfileHeader extends ImmutablePureComponent { const isMyProfile = !account ? false : account.get('id') === me if (isMyProfile) { tabs.push({ - to: `/${account.get('acct')}/bookmarks`, + to: `/${account.get('acct')}/likes`, + title: 'Likes', + }) + tabs.push({ + to: `/${account.get('acct')}/bookmark_collections`, title: intl.formatMessage(messages.bookmarks), }) } @@ -149,21 +153,12 @@ class ProfileHeader extends ImmutablePureComponent { displayNone: stickied, }) - const mobileAvatarContainerClasses = CX({ - d: 1, - circle: 1, - boxShadowProfileAvatar: 1, - mtNeg50PX: !headerMissing, - }) - const mobileDescriptionContainerClasses = CX({ d: 1, w100PC: 1, px15: 1, - mt5: !!me, mb10: 1, pt15: !!me, - pb10: 1, }) return ( @@ -174,10 +169,10 @@ class ProfileHeader extends ImmutablePureComponent {
{ !headerMissing && -
+
{intl.formatMessage(messages.headerPhoto)} @@ -185,85 +180,80 @@ class ProfileHeader extends ImmutablePureComponent { } { headerMissing && -
+
}
-
-
- +
+
+
+ +
+ { + account && account.get('id') === me && +
+ +
+ } + + { + account && account.get('id') !== me && !!me && +
+
+
+ +
+
+ }
-
+
-
- { - account && account.get('id') === me && -
- -
- } - - { - account && account.get('id') !== me && !!me && -
- -
- -
- } -
{children}
-
+
diff --git a/app/javascript/gabsocial/components/sidebar/deck_sidebar.js b/app/javascript/gabsocial/components/sidebar/deck_sidebar.js index 456d2140..235f6fbc 100644 --- a/app/javascript/gabsocial/components/sidebar/deck_sidebar.js +++ b/app/javascript/gabsocial/components/sidebar/deck_sidebar.js @@ -89,18 +89,12 @@ class DeckSidebar extends ImmutablePureComponent {
{ + /* !!gabDeckOrder && gabDeckOrder.map((item, i) => ( -
diff --git a/app/javascript/gabsocial/components/user_stat.js b/app/javascript/gabsocial/components/user_stat.js index f91b7ca5..119b80ef 100644 --- a/app/javascript/gabsocial/components/user_stat.js +++ b/app/javascript/gabsocial/components/user_stat.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import { NavLink } from 'react-router-dom' import { CX } from '../constants' import Text from './text' +import DotTextSeperator from './dot_text_seperator' /** * Renders a user stat component @@ -30,17 +31,29 @@ class UserStat extends React.PureComponent { title, value, isCentered, + isInline, + isLast, } = this.props const { hovering } = this.state const align = isCentered ? 'center' : 'left' + const titleSize = isInline ? 'normal' : 'extraLarge' + const subtitleSize = isInline ? 'normal' : 'small' + const containerClasses = CX({ d: 1, cursorPointer: 1, noUnderline: 1, flexNormal: isCentered, - flexGrow1: !isCentered, - pr15: !isCentered, + flexGrow1: !isCentered && !isInline, + flexRow: isInline, + aiCenter: isInline, + pr15: !isCentered && !isInline, + pr10: !isCentered && isInline, + }) + const subtitleClasses = CX({ + pr5: isInline, + pl5: isInline, }) return ( @@ -51,12 +64,13 @@ class UserStat extends React.PureComponent { onMouseEnter={this.handleOnMouseEnter} onMouseLeave={this.handleOnMouseLeave} > - + {value} - + {title} + { !isLast && isInline && } ) } @@ -72,6 +86,8 @@ UserStat.propTypes = { PropTypes.object, ]).isRequired, isCentered: PropTypes.bool, + isInline: PropTypes.bool, + isLast: PropTypes.bool, } export default UserStat \ No newline at end of file diff --git a/app/javascript/gabsocial/constants.js b/app/javascript/gabsocial/constants.js index 31c593a7..ef3f4e57 100644 --- a/app/javascript/gabsocial/constants.js +++ b/app/javascript/gabsocial/constants.js @@ -46,11 +46,12 @@ export const POPOVER_TIMELINE_INJECTION_OPTIONS = 'TIMELINE_INJECTION_OPTIONS' export const POPOVER_USER_INFO = 'USER_INFO' export const POPOVER_VIDEO_STATS = 'VIDEO_STATS' +export const MODAL_ALBUM_CREATE = 'ALBUM_CREATE' export const MODAL_BLOCK_ACCOUNT = 'BLOCK_ACCOUNT' +export const MODAL_BOOKMARK_COLLECTION_CREATE = 'BOOKMARK_COLLECTION_CREATE' export const MODAL_BOOST = 'BOOST' export const MODAL_CHAT_CONVERSATION_CREATE = 'CHAT_CONVERSATION_CREATE' export const MODAL_CHAT_CONVERSATION_DELETE = 'CHAT_CONVERSATION_DELETE' -export const MODAL_COMMUNITY_TIMELINE_SETTINGS = 'COMMUNITY_TIMELINE_SETTINGS' export const MODAL_COMPOSE = 'COMPOSE' export const MODAL_CONFIRM = 'CONFIRM' export const MODAL_DECK_COLUMN_ADD = 'DECK_COLUMN_ADD' @@ -62,7 +63,6 @@ export const MODAL_EMAIL_CONFIRMATION_REMINDER = 'EMAIL_CONFIRMATION_REMINDER' export const MODAL_GROUP_CREATE = 'GROUP_CREATE' export const MODAL_GROUP_DELETE = 'GROUP_DELETE' export const MODAL_GROUP_PASSWORD = 'GROUP_PASSWORD' -export const MODAL_HASHTAG_TIMELINE_SETTINGS = 'HASHTAG_TIMELINE_SETTINGS' export const MODAL_HOME_TIMELINE_SETTINGS = 'HOME_TIMELINE_SETTINGS' export const MODAL_HOTKEYS = 'HOTKEYS' export const MODAL_LIST_ADD_USER = 'LIST_ADD_USER' diff --git a/app/javascript/gabsocial/features/account_albums.js b/app/javascript/gabsocial/features/account_albums.js new file mode 100644 index 00000000..5f7f7fd6 --- /dev/null +++ b/app/javascript/gabsocial/features/account_albums.js @@ -0,0 +1,150 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import ImmutablePropTypes from 'react-immutable-proptypes' +import ImmutablePureComponent from 'react-immutable-pure-component' +import { injectIntl, defineMessages } from 'react-intl' +import { expandAccountMediaTimeline } from '../actions/timelines' +import { getAccountGallery } from '../selectors' +import ColumnIndicator from '../components/column_indicator' +import MediaItem from '../components/media_item' +import LoadMore from '../components/load_more' +import Block from '../components/block' +import MediaGalleryPlaceholder from '../components/placeholder/media_gallery_placeholder' + +class AccountAlbums extends ImmutablePureComponent { + + componentDidMount() { + const { accountId, mediaType } = this.props + + if (accountId && accountId !== -1) { + this.props.dispatch(expandAccountMediaTimeline(accountId, { mediaType })) + } + } + + componentWillReceiveProps(nextProps) { + if ( + (nextProps.accountId && nextProps.accountId !== this.props.accountId) || + (nextProps.accountId && nextProps.mediaType !== this.props.mediaType) + ) { + this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { + mediaType: nextProps.mediaType, + })) + } + } + + handleScrollToBottom = () => { + if (this.props.hasMore) { + this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined) + } + } + + handleScroll = (e) => { + const { scrollTop, scrollHeight, clientHeight } = e.target + const offset = scrollHeight - scrollTop - clientHeight + + if (150 > offset && !this.props.isLoading) { + this.handleScrollToBottom() + } + } + + handleLoadMore = (maxId) => { + if (this.props.accountId && this.props.accountId !== -1) { + this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { + maxId, + mediaType: this.props.mediaType, + })) + } + } + + handleLoadOlder = (e) => { + e.preventDefault() + this.handleScrollToBottom() + } + + render() { + const { + attachments, + isLoading, + hasMore, + intl, + account, + } = this.props + + if (!account) return null + + return ( + +
+ + { + attachments.map((attachment, i) => ( + + )) + } + + { + isLoading && attachments.size === 0 && +
+ +
+ } + + { + !isLoading && attachments.size === 0 && + + } +
+ + { + hasMore && !(isLoading && attachments.size === 0) && + + } +
+ ) + } + +} + +const messages = defineMessages({ + none: { id: 'account_gallery.none', defaultMessage: 'No media to show.' }, +}) + +const mapStateToProps = (state, { account, mediaType }) => { + const accountId = !!account ? account.get('id') : -1 + + return { + accountId, + attachments: getAccountGallery(state, accountId, mediaType), + isLoading: state.getIn(['timelines', `account:${accountId}:media`, 'isLoading']), + hasMore: state.getIn(['timelines', `account:${accountId}:media`, 'hasMore']), + } +} + +const mapDispatchToProps = (dispatch) => ({ + +}) + +AccountAlbums.propTypes = { + dispatch: PropTypes.func.isRequired, + account: ImmutablePropTypes.map, + accountId: PropTypes.string, + attachments: ImmutablePropTypes.list.isRequired, + isLoading: PropTypes.bool, + hasMore: PropTypes.bool, + intl: PropTypes.object.isRequired, + mediaType: PropTypes.oneOf([ + 'photo', + 'video', + ]), +} + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(AccountAlbums)) \ No newline at end of file diff --git a/app/javascript/gabsocial/features/album_create.js b/app/javascript/gabsocial/features/album_create.js new file mode 100644 index 00000000..e69de29b diff --git a/app/javascript/gabsocial/features/bookmark_collection_create.js b/app/javascript/gabsocial/features/bookmark_collection_create.js new file mode 100644 index 00000000..9f381e01 --- /dev/null +++ b/app/javascript/gabsocial/features/bookmark_collection_create.js @@ -0,0 +1,73 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { defineMessages, injectIntl } from 'react-intl' +import { changeListEditorTitle, submitListEditor } from '../actions/lists' +import { closeModal } from '../actions/modal' +import { MODAL_LIST_CREATE } from '../constants' +import Button from '../components/button' +import Input from '../components/input' +import Form from '../components/form' +import Text from '../components/text' + +class BookmarkCollectionCreate extends React.PureComponent { + + state = { + value: '', + } + + onChange = (value) => { + this.setState({ value }) + } + + handleOnSubmit = () => { + this.props.onSubmit() + } + + render() { + const { disabled, isModal } = this.props + const { value } = this.state + + const isDisabled = !value || disabled + + return ( +
+ + + +
+ ) + } + +} + +const mapStateToProps = (state) => ({ + disabled: state.getIn(['listEditor', 'isSubmitting']), +}) + +const mapDispatchToProps = (dispatch, { isModal }) => ({ + onSubmit() { + if (isModal) dispatch(closeModal(MODAL_LIST_CREATE)) + dispatch(submitListEditor(true)) + }, +}) + +BookmarkCollectionCreate.propTypes = { + onSubmit: PropTypes.func.isRequired, + isModal: PropTypes.bool, +} + +export default connect(mapStateToProps, mapDispatchToProps)(BookmarkCollectionCreate) \ No newline at end of file diff --git a/app/javascript/gabsocial/features/bookmark_collections.js b/app/javascript/gabsocial/features/bookmark_collections.js index fc9c47ec..eb1cff3e 100644 --- a/app/javascript/gabsocial/features/bookmark_collections.js +++ b/app/javascript/gabsocial/features/bookmark_collections.js @@ -3,8 +3,18 @@ import PropTypes from 'prop-types' import { connect } from 'react-redux' import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePropTypes from 'react-immutable-proptypes' +import { + MODAL_BOOKMARK_COLLECTION_CREATE, +} from '../constants' +import { + meUsername, +} from '../initial_state' import { fetchBookmarkCollections } from '../actions/bookmarks' +import { openModal } from '../actions/modal' import ColumnIndicator from '../components/column_indicator' +import Block from '../components/block' +import Button from '../components/button' +import Text from '../components/text' import List from '../components/list' class BookmarkCollections extends ImmutablePureComponent { @@ -13,6 +23,10 @@ class BookmarkCollections extends ImmutablePureComponent { this.props.onFetchBookmarkCollections() } + handleOpenModal = () => { + this.props.onOpenModal() + } + render() { const { isLoading, @@ -24,19 +38,32 @@ class BookmarkCollections extends ImmutablePureComponent { return } - const listItems = shortcuts.map((s) => ({ + const listItems = [{ to: `/${meUsername}/bookmark_collections/bookmarks`, title: 'Bookmarks' }].concat(!!bookmarkCollections ? bookmarkCollections.map((s) => ({ to: s.get('to'), title: s.get('title'), - image: s.get('image'), - })) + })) : []) return ( - + +
+
+ Bookmark Collections +
+
+ +
) } @@ -49,6 +76,9 @@ const mapStateToProps = (state) => ({ }) const mapDispatchToProps = (dispatch) => ({ + onOpenModal() { + dispatch(openModal(MODAL_BOOKMARK_COLLECTION_CREATE)) + }, onFetchBookmarkCollections() { dispatch(fetchBookmarkCollections()) }, @@ -58,6 +88,7 @@ BookmarkCollections.propTypes = { isLoading: PropTypes.bool.isRequired, isError: PropTypes.bool.isRequired, onFetchBookmarkCollections: PropTypes.func.isRequired, + onOpenModal: PropTypes.func.isRequired, bookmarkCollections: ImmutablePropTypes.list, } diff --git a/app/javascript/gabsocial/features/bookmarked_statuses.js b/app/javascript/gabsocial/features/bookmarked_statuses.js index df826eb9..59a6683f 100644 --- a/app/javascript/gabsocial/features/bookmarked_statuses.js +++ b/app/javascript/gabsocial/features/bookmarked_statuses.js @@ -13,11 +13,11 @@ import ColumnIndicator from '../components/column_indicator' class BookmarkedStatuses extends ImmutablePureComponent { componentWillMount() { - this.props.dispatch(fetchBookmarkedStatuses()) + this.props.dispatch(fetchBookmarkedStatuses(this.props.bookmarkCollectionId)) } handleLoadMore = debounce(() => { - this.props.dispatch(expandBookmarkedStatuses()) + this.props.dispatch(expandBookmarkedStatuses(this.props.bookmarkCollectionId)) }, 300, { leading: true }) render() { @@ -46,14 +46,13 @@ class BookmarkedStatuses extends ImmutablePureComponent { } -const mapStateToProps = (state, { params: { username } }) => { - return { - isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()), - statusIds: state.getIn(['status_lists', 'bookmarks', 'items']), - isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true), - hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']), - } -} +const mapStateToProps = (state, { params: { username, bookmarkCollectionId } }) => ({ + bookmarkCollectionId, + isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()), + statusIds: state.getIn(['status_lists', 'bookmarks', 'items']), + isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true), + hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']), +}) BookmarkedStatuses.propTypes = { dispatch: PropTypes.func.isRequired, diff --git a/app/javascript/gabsocial/features/chat_conversation_create.js b/app/javascript/gabsocial/features/chat_conversation_create.js index 6d99053f..4e7660be 100644 --- a/app/javascript/gabsocial/features/chat_conversation_create.js +++ b/app/javascript/gabsocial/features/chat_conversation_create.js @@ -21,7 +21,6 @@ class ChatConversationCreate extends React.PureComponent { } handleOnCreateChatConversation = (accountId) => { - console.log("handleOnCreateChatConversation:", accountId) this.props.onCreateChatConversation(accountId) } @@ -69,7 +68,6 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ onChange: (value) => { - console.log("value", value) dispatch(fetchChatConversationAccountSuggestions(value)) }, onCreateChatConversation: (accountId) => { diff --git a/app/javascript/gabsocial/features/chat_conversation_muted_accounts.js b/app/javascript/gabsocial/features/chat_conversation_muted_accounts.js deleted file mode 100644 index 29057928..00000000 --- a/app/javascript/gabsocial/features/chat_conversation_muted_accounts.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { injectIntl, FormattedMessage } from 'react-intl' -import ImmutablePureComponent from 'react-immutable-pure-component' -import ImmutablePropTypes from 'react-immutable-proptypes' -import debounce from 'lodash.debounce' -import { me } from '../initial_state' -import { - fetchChatMessengerMutes, - expandChatMessengerMutes, - unmuteChatMessenger, -} from '../actions/chat_conversation_accounts' -import Account from '../components/account' -import Block from '../components/block' -import BlockHeading from '../components/block_heading' -import ScrollableList from '../components/scrollable_list' - -class ChatConversationMutedAccounts extends ImmutablePureComponent { - - componentWillMount() { - this.props.onFetchMutes() - } - - handleLoadMore = debounce(() => { - this.props.onExpandMutes() - }, 300, { leading: true }) - - render() { - const { - accountIds, - hasMore, - isLoading, - } = this.props - - return ( -
-
- } /> -
- } - > - { - accountIds && accountIds.map((id) => - this.props.onRemove(id)} - actionTitle='Remove' - /> - ) - } - -
- ) - } - -} - -const mapStateToProps = (state) => ({ - accountIds: state.getIn(['user_lists', 'chat_mutes', me, 'items']), - hasMore: !!state.getIn(['user_lists', 'chat_mutes', me, 'next']), - isLoading: state.getIn(['user_lists', 'chat_mutes', me, 'isLoading']), -}) - -const mapDispatchToProps = (dispatch) => ({ - onFetchMutes: () => dispatch(fetchChatMessengerMutes()), - onExpandMutes: () => dispatch(expandChatMessengerMutes()), - onRemove: (accountId) => dispatch(unmuteChatMessenger(accountId)), -}) - -ChatConversationMutedAccounts.propTypes = { - accountIds: ImmutablePropTypes.list, - hasMore: PropTypes.bool, - isLoading: PropTypes.bool, - onExpandMutes: PropTypes.func.isRequired, - onFetchMutes: PropTypes.func.isRequired, -} - -export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ChatConversationMutedAccounts)) \ No newline at end of file diff --git a/app/javascript/gabsocial/features/compose/components/compose_destination_header.js b/app/javascript/gabsocial/features/compose/components/compose_destination_header.js index 8ca6879e..818f10cf 100644 --- a/app/javascript/gabsocial/features/compose/components/compose_destination_header.js +++ b/app/javascript/gabsocial/features/compose/components/compose_destination_header.js @@ -30,34 +30,38 @@ class ComposeDestinationHeader extends ImmutablePureComponent { } render() { - const { account, isModal } = this.props + const { account, isModal, formLocation } = this.props + const isIntroduction = formLocation === 'introduction' const title = 'Post to timeline' return (
-
- -
+ { + !isIntroduction && +
+ +
+ }
{ - !isModal && + !isModal && !isIntroduction &&
diff --git a/app/javascript/gabsocial/layouts/profile_layout.js b/app/javascript/gabsocial/layouts/profile_layout.js index 892d24c5..7027072c 100644 --- a/app/javascript/gabsocial/layouts/profile_layout.js +++ b/app/javascript/gabsocial/layouts/profile_layout.js @@ -110,7 +110,7 @@ class ProfileLayout extends ImmutablePureComponent {
-
+
{children}
diff --git a/app/javascript/gabsocial/pages/community_page.js b/app/javascript/gabsocial/pages/community_page.js index 9ce14539..7b59c541 100644 --- a/app/javascript/gabsocial/pages/community_page.js +++ b/app/javascript/gabsocial/pages/community_page.js @@ -1,11 +1,8 @@ import React from 'react' import PropTypes from 'prop-types' -import { connect } from 'react-redux' import { defineMessages, injectIntl } from 'react-intl' -import { openModal } from '../actions/modal' import PageTitle from '../features/ui/util/page_title' import DefaultLayout from '../layouts/default_layout' -import { MODAL_COMMUNITY_TIMELINE_SETTINGS } from '../constants' import { LinkFooter, GroupsPanel, @@ -16,10 +13,6 @@ import { class CommunityPage extends React.PureComponent { - onOpenCommunityPageSettingsModal = () => { - this.props.dispatch(openModal(MODAL_COMMUNITY_TIMELINE_SETTINGS)) - } - render() { const { children, intl } = this.props @@ -29,12 +22,6 @@ class CommunityPage extends React.PureComponent { { - const { params } = this.props - - const hashtag = isObject(params) ? params.id : '' - if (!hashtag) return - - this.props.dispatch(openModal(MODAL_HASHTAG_TIMELINE_SETTINGS, { - hashtag, - })) - } - render() { const { children, @@ -41,12 +27,6 @@ class HashtagPage extends React.PureComponent { showBackBtn title={intl.formatMessage(messages.hashtagTimeline)} page={`hashtag.${hashtag}`} - actions={[ - { - icon: 'ellipsis', - onClick: this.onOpenHashtagPageSettingsModal, - }, - ]} layout={[ ProgressPanel, TrendsBreakingPanel, @@ -73,4 +53,4 @@ HashtagPage.propTypes = { params: PropTypes.object.isRequired, } -export default injectIntl(connect()(HashtagPage)) \ No newline at end of file +export default injectIntl(HashtagPage) \ No newline at end of file diff --git a/app/javascript/gabsocial/reducers/relationships.js b/app/javascript/gabsocial/reducers/relationships.js index e2a08d17..6e804054 100644 --- a/app/javascript/gabsocial/reducers/relationships.js +++ b/app/javascript/gabsocial/reducers/relationships.js @@ -10,41 +10,47 @@ import { ACCOUNT_MUTE_SUCCESS, ACCOUNT_UNMUTE_SUCCESS, RELATIONSHIPS_FETCH_SUCCESS, -} from '../actions/accounts'; -import { Map as ImmutableMap, fromJS } from 'immutable'; +} from '../actions/accounts' +import { + BLOCK_CHAT_MESSAGER_SUCCESS, + UNBLOCK_CHAT_MESSAGER_SUCCESS, +} from '../actions/chat_conversation_accounts' +import { Map as ImmutableMap, fromJS } from 'immutable' -const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship)); +const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship)) const normalizeRelationships = (state, relationships) => { relationships.forEach(relationship => { - state = normalizeRelationship(state, relationship); - }); + state = normalizeRelationship(state, relationship) + }) - return state; -}; + return state +} -const initialState = ImmutableMap(); +const initialState = ImmutableMap() export default function relationships(state = initialState, action) { switch(action.type) { case ACCOUNT_FOLLOW_REQUEST: - return state.setIn([action.id, action.locked ? 'requested' : 'following'], true); + return state.setIn([action.id, action.locked ? 'requested' : 'following'], true) case ACCOUNT_FOLLOW_FAIL: - return state.setIn([action.id, action.locked ? 'requested' : 'following'], false); + return state.setIn([action.id, action.locked ? 'requested' : 'following'], false) case ACCOUNT_UNFOLLOW_REQUEST: - return state.setIn([action.id, 'following'], false); + return state.setIn([action.id, 'following'], false) case ACCOUNT_UNFOLLOW_FAIL: - return state.setIn([action.id, 'following'], true); + return state.setIn([action.id, 'following'], true) case ACCOUNT_FOLLOW_SUCCESS: case ACCOUNT_UNFOLLOW_SUCCESS: case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_UNBLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: case ACCOUNT_UNMUTE_SUCCESS: - return normalizeRelationship(state, action.relationship); + case BLOCK_CHAT_MESSAGER_SUCCESS: + case UNBLOCK_CHAT_MESSAGER_SUCCESS: + return normalizeRelationship(state, action.relationship) case RELATIONSHIPS_FETCH_SUCCESS: - return normalizeRelationships(state, action.relationships); + return normalizeRelationships(state, action.relationships) default: - return state; + return state } -}; +} \ No newline at end of file diff --git a/app/javascript/gabsocial/reducers/settings.js b/app/javascript/gabsocial/reducers/settings.js index 4080a673..513d9d1b 100644 --- a/app/javascript/gabsocial/reducers/settings.js +++ b/app/javascript/gabsocial/reducers/settings.js @@ -50,18 +50,12 @@ const initialState = ImmutableMap({ theme: 'white', }), - home: ImmutableMap({ - shows: ImmutableMap({ - reply: true, - repost: true, - }), - }), - - community: ImmutableMap({ - shows: ImmutableMap({ - onlyMedia: false, - }), - }), + // home: ImmutableMap({ + // shows: ImmutableMap({ + // reply: true, + // repost: true, + // }), + // }), }) const defaultColumns = fromJS([ diff --git a/app/javascript/gabsocial/reducers/toasts.js b/app/javascript/gabsocial/reducers/toasts.js index 8e5479dc..a52d32fa 100644 --- a/app/javascript/gabsocial/reducers/toasts.js +++ b/app/javascript/gabsocial/reducers/toasts.js @@ -5,14 +5,19 @@ import { } from '../actions/toasts' import { Map as ImmutableMap, List as ImmutableList } from 'immutable' +const makeMessageFromData = (data) => { + return `${data.type}`.split('_').join(' ').toLowerCase() +} + const initialState = ImmutableList([]) export default function toasts(state = initialState, action) { switch(action.type) { case TOAST_SHOW: + console.log("action:", action) return state.set(state.size, ImmutableMap({ key: state.size > 0 ? state.last().get('key') + 1 : 0, - message: 'action.message', + message: makeMessageFromData(action.toastData), type: action.toastType, })) case TOAST_DISMISS: diff --git a/app/javascript/gabsocial/reducers/user_lists.js b/app/javascript/gabsocial/reducers/user_lists.js index 7b68b822..637ccf08 100644 --- a/app/javascript/gabsocial/reducers/user_lists.js +++ b/app/javascript/gabsocial/reducers/user_lists.js @@ -62,13 +62,6 @@ import { CHAT_MESSENGER_BLOCKS_EXPAND_REQUEST, CHAT_MESSENGER_BLOCKS_EXPAND_SUCCESS, CHAT_MESSENGER_BLOCKS_EXPAND_FAIL, - - CHAT_MESSENGER_MUTES_FETCH_REQUEST, - CHAT_MESSENGER_MUTES_FETCH_SUCCESS, - CHAT_MESSENGER_MUTES_FETCH_FAIL, - CHAT_MESSENGER_MUTES_EXPAND_REQUEST, - CHAT_MESSENGER_MUTES_EXPAND_SUCCESS, - CHAT_MESSENGER_MUTES_EXPAND_FAIL, } from '../actions/chat_conversation_accounts' import { GROUP_MEMBERS_FETCH_SUCCESS, @@ -243,17 +236,6 @@ export default function userLists(state = initialState, action) { case CHAT_MESSENGER_BLOCKS_EXPAND_FAIL: return setListFailed(state, 'chat_blocks', me) - case CHAT_MESSENGER_MUTES_FETCH_REQUEST: - case CHAT_MESSENGER_MUTES_EXPAND_REQUEST: - return state.setIn(['chat_mutes', me, 'isLoading'], true) - case CHAT_MESSENGER_MUTES_FETCH_SUCCESS: - return normalizeList(state, 'chat_mutes', me, action.accounts, action.next) - case CHAT_MESSENGER_MUTES_EXPAND_SUCCESS: - return appendToList(state, 'chat_mutes', me, action.accounts, action.next) - case CHAT_MESSENGER_MUTES_FETCH_FAIL: - case CHAT_MESSENGER_MUTES_EXPAND_FAIL: - return setListFailed(state, 'chat_mutes', me) - default: return state; } diff --git a/app/javascript/gabsocial/selectors/index.js b/app/javascript/gabsocial/selectors/index.js index de94a738..e9838f5c 100644 --- a/app/javascript/gabsocial/selectors/index.js +++ b/app/javascript/gabsocial/selectors/index.js @@ -232,7 +232,7 @@ export const getToasts = createSelector([ let arr = [] - base.forEach(item => { + base.forEach((item) => { arr.push({ message: item.get('message'), type: item.get('type'), @@ -241,4 +241,20 @@ export const getToasts = createSelector([ }) return arr +}) + +export const getListOfGroups = createSelector([ + (state) => state.get('groups'), + (state, { type }) => state.getIn(['group_lists', type, 'items']), +], (groups, groupIds) => { + console.log("groupIds:", groupIds) + let list = ImmutableList() + groupIds.forEach((id, i) => { + const group = groups.get(`${id}`) + console.log("groupIds:", id, i, group) + list = list.set(i, group) + }) + console.log("list:", list) + + return list }) \ No newline at end of file diff --git a/app/javascript/styles/global.css b/app/javascript/styles/global.css index 5aecc1dc..20efb9dd 100644 --- a/app/javascript/styles/global.css +++ b/app/javascript/styles/global.css @@ -576,6 +576,7 @@ pre { .h220PX { height: 220px; } .h215PX { height: 215px; } .h200PX { height: 200px; } +.h172PX { height: 172px; } .h158PX { height: 158px; } .h122PX { height: 122px; } .h60PX { height: 60px; } diff --git a/app/models/chat_conversation_account.rb b/app/models/chat_conversation_account.rb index 629f749b..fd89dcf0 100644 --- a/app/models/chat_conversation_account.rb +++ b/app/models/chat_conversation_account.rb @@ -14,6 +14,7 @@ # updated_at :datetime not null # unread_count :bigint(8) default(0), not null # chat_message_expiration_policy :string +# is_muted :boolean default(FALSE), not null # # : todo : expires diff --git a/app/models/chat_mute.rb b/app/models/chat_mute.rb deleted file mode 100644 index 6c648a02..00000000 --- a/app/models/chat_mute.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: chat_mutes -# -# id :bigint(8) not null, primary key -# account_id :integer not null -# target_account_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null -# - -class ChatMute < ApplicationRecord - include Paginable - include RelationshipCacheable - - belongs_to :account - belongs_to :target_account, class_name: 'Account' - - validates :account_id, uniqueness: { scope: :target_account_id } - - after_commit :remove_blocking_cache - - private - - def remove_blocking_cache - Rails.cache.delete("exclude_chat_account_ids_for:#{account_id}") - end -end diff --git a/app/models/link_block.rb b/app/models/link_block.rb index 8655276f..95e22a30 100644 --- a/app/models/link_block.rb +++ b/app/models/link_block.rb @@ -22,6 +22,9 @@ class LinkBlock < ApplicationRecord Addressable::URI.parse(array[0]).normalize } url = urls.first + + return false if url.nil? + link_for_fetch = TagManager.instance.normalize_link(url) link_for_fetch = link_for_fetch.chomp("/") diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb index 02929e45..bdc26997 100644 --- a/app/presenters/account_relationships_presenter.rb +++ b/app/presenters/account_relationships_presenter.rb @@ -27,6 +27,9 @@ class AccountRelationshipsPresenter private + # : todo : + # chat muting, chat blocking + def cached return @cached if defined?(@cached) diff --git a/app/serializers/rest/chat_conversation_account_serializer.rb b/app/serializers/rest/chat_conversation_account_serializer.rb index f1ea1b42..307de39c 100644 --- a/app/serializers/rest/chat_conversation_account_serializer.rb +++ b/app/serializers/rest/chat_conversation_account_serializer.rb @@ -3,7 +3,7 @@ class REST::ChatConversationAccountSerializer < ActiveModel::Serializer attributes :id, :is_hidden, :is_approved, :unread_count, :is_unread, :chat_conversation_id, :created_at, - :is_blocked, :is_muted, :chat_message_expiration_policy + :is_muted, :chat_message_expiration_policy has_many :participant_accounts, key: :other_accounts, serializer: REST::AccountSerializer has_one :last_chat_message, serializer: REST::ChatMessageSerializer, unless: :last_chat_message_id? @@ -24,12 +24,4 @@ class REST::ChatConversationAccountSerializer < ActiveModel::Serializer object.unread_count > 0 end - def is_blocked - false - end - - def is_muted - false - end - end diff --git a/app/services/mute_chat_messenger_service.rb b/app/services/mute_chat_messenger_service.rb deleted file mode 100644 index 52210d5d..00000000 --- a/app/services/mute_chat_messenger_service.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class MuteChatMessengerService < BaseService - def call(account, target_account) - return if account.id == target_account.id - mute = account.chat_mute!(target_account) - mute - end -end diff --git a/app/services/post_chat_message_service.rb b/app/services/post_chat_message_service.rb index 2b2cfcbc..084b61a3 100644 --- a/app/services/post_chat_message_service.rb +++ b/app/services/post_chat_message_service.rb @@ -52,16 +52,17 @@ class PostChatMessageService < BaseService @chat_conversation_recipients_accounts = ChatConversationAccount.where(chat_conversation: @chat_conversation) @chat_conversation_recipients_accounts.each do |recipient| recipient.last_chat_message_id = @chat.id - recipient.is_hidden = false # reset to show unless blocked + recipient.is_hidden = false # : todo : reset to show unless blocked # Get not mine if @account_conversation.id != recipient.id recipient.unread_count = recipient.unread_count + 1 - # : todo : - # check if muting, redis - payload = InlineRenderer.render(@chat, recipient.account, :chat_message) - Redis.current.publish("chat_messages:#{recipient.account.id}", Oj.dump(event: :notification, payload: payload)) + # check if muting + unless recipient.is_muted + payload = InlineRenderer.render(@chat, recipient.account, :chat_message) + Redis.current.publish("chat_messages:#{recipient.account.id}", Oj.dump(event: :notification, payload: payload)) + end else recipient.unread_count = 0 end @@ -80,8 +81,10 @@ class PostChatMessageService < BaseService raise ActiveRecord::RecordInvalid end - def set_message_expiration_date - case @account_conversation.expiration_policy + def set_message_expiration_date! + @expires_at = nil + + case @account_conversation.chat_message_expiration_policy when :five_minutes @expires_at = 5.minutes when :sixty_minutes diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 5a8eae2b..dcb5ccc6 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -111,6 +111,8 @@ class PostStatusService < BaseService return if group_id.blank? return if @autoJoinGroup + # : todo : check removedaccounts if exist dont allow + raise GabSocial::ValidationError, I18n.t('statuses.not_a_member_of_group') if not GroupAccount.where(account: @account, group_id: group_id).exists? end diff --git a/app/services/unmute_chat_messenger_service.rb b/app/services/unmute_chat_messenger_service.rb deleted file mode 100644 index 95e65dc2..00000000 --- a/app/services/unmute_chat_messenger_service.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -class UnmuteChatMessengerService < BaseService - def call(account, target_account) - return unless account.chat_muting?(target_account) - account.chat_unmute!(target_account) - end -end diff --git a/config/routes.rb b/config/routes.rb index ca2019e5..e045cbd8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -228,15 +228,14 @@ Rails.application.routes.draw do resource :chat_conversation_accounts, only: :show do resource :blocked_chat_accounts, only: :show, controller: 'chat_conversation_accounts/blocked_chat_accounts' - resource :muted_chat_accounts, only: :show, controller: 'chat_conversation_accounts/muted_chat_accounts' member do get :is_messenger_blocked get :is_messenger_muted post :block_messenger post :unblock_messenger - post :mute_messenger - post :unmute_messenger + post :mute_chat_conversation + post :unmute_chat_conversation post :set_expiration_policy end end @@ -392,6 +391,13 @@ Rails.application.routes.draw do get '/', to: 'react#react', as: :homepage + get '/about', to: 'react#react' + get '/about/tos', to: 'react#react' + get '/about/privacy', to: 'react#react' + get '/about/investors', to: 'react#react' + get '/about/dmca', to: 'react#react' + get '/about/sales', to: 'react#react' + match '*unmatched_route', via: :all, to: 'application#raise_not_found', diff --git a/db/migrate/20201216045306_add_is_muted_to_chat_conversation_accounts.rb b/db/migrate/20201216045306_add_is_muted_to_chat_conversation_accounts.rb new file mode 100644 index 00000000..dedb5315 --- /dev/null +++ b/db/migrate/20201216045306_add_is_muted_to_chat_conversation_accounts.rb @@ -0,0 +1,9 @@ +class AddIsMutedToChatConversationAccounts < ActiveRecord::Migration[5.2] + def up + safety_assured { add_column :chat_conversation_accounts, :is_muted, :bool, default: false, null: false } + end + + def down + remove_column :chat_conversation_accounts, :is_muted + end +end diff --git a/db/migrate/20201216051551_remove_chat_mutes.rb b/db/migrate/20201216051551_remove_chat_mutes.rb new file mode 100644 index 00000000..7635292d --- /dev/null +++ b/db/migrate/20201216051551_remove_chat_mutes.rb @@ -0,0 +1,5 @@ +class RemoveChatMutes < ActiveRecord::Migration[5.2] + def change + drop_table :chat_mutes + end +end diff --git a/db/schema.rb b/db/schema.rb index ca75cd76..5ba6db65 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_12_15_203113) do +ActiveRecord::Schema.define(version: 2020_12_16_051551) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -214,6 +214,7 @@ ActiveRecord::Schema.define(version: 2020_12_15_203113) do t.datetime "updated_at", null: false t.bigint "unread_count", default: 0, null: false t.string "chat_message_expiration_policy" + t.boolean "is_muted", default: false, null: false t.index ["account_id"], name: "index_chat_conversation_accounts_on_account_id" t.index ["chat_conversation_id"], name: "index_chat_conversation_accounts_on_chat_conversation_id" end @@ -234,14 +235,6 @@ ActiveRecord::Schema.define(version: 2020_12_15_203113) do t.index ["from_account_id", "chat_conversation_id"], name: "index_chat_messages_on_from_account_id_and_chat_conversation_id" end - create_table "chat_mutes", force: :cascade do |t| - t.integer "account_id", null: false - t.integer "target_account_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["account_id", "target_account_id"], name: "index_chat_mutes_on_account_id_and_target_account_id", unique: true - end - create_table "conversations", force: :cascade do |t| t.string "uri" t.datetime "created_at", null: false