diff --git a/app/controllers/admin/chat_conversations_controller.rb b/app/controllers/admin/chat_conversations_controller.rb new file mode 100644 index 00000000..087f1d7a --- /dev/null +++ b/app/controllers/admin/chat_conversations_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class ChatConversationsController < BaseController + before_action :set_account + + PER_PAGE = 20 + + def index + authorize :account, :index? + @chatConversationAccounts = ChatConversationAccount.where(account: @account).page(params[:page]).per(PER_PAGE) + end + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/admin/chat_messages_controller.rb b/app/controllers/admin/chat_messages_controller.rb new file mode 100644 index 00000000..212c645a --- /dev/null +++ b/app/controllers/admin/chat_messages_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class ChatMessagesController < BaseController + before_action :set_account + + PER_PAGE = 100 + + def index + authorize :account, :index? + @followers = ChatMessage.where(from_account: @account).page(params[:page]).per(PER_PAGE) + end + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index fd7c9d6f..44b3ce02 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -5,6 +5,9 @@ module Admin class DashboardController < BaseController def index @users_count = User.count + @statuses_count = Status.count + @pro_accounts_count = Account.where(is_pro: true).count + @donor_accounts_count = Account.where(is_donor: true).count @registrations_week = Redis.current.get("activity:accounts:local:#{current_week}") || 0 @logins_week = Redis.current.pfcount("activity:logins:#{current_week}") @interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0 diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 18519287..efac4830 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,67 +1,75 @@ # frozen_string_literal: true module Admin - class GroupsController < BaseController - before_action :set_group, except: [:index] - before_action :set_filter_params - - def index - authorize :group, :index? - @groups = filtered_groups.page(params[:page]) - end - - def destroy - authorize @group, :destroy? - @group.destroy! - log_action :destroy, @group - flash[:notice] = I18n.t('admin.groups.destroyed_msg') - redirect_to admin_groups_path(page: params[:page], **@filter_params) - end - - def enable_featured - authorize @group, :update? - @group.is_featured = true - @group.save! - log_action :update, @group - flash[:notice] = I18n.t('admin.groups.updated_msg') - redirect_to admin_groups_path(page: params[:page], **@filter_params) - end - - def disable_featured - authorize @group, :update? - @group.is_featured = false - @group.save! - log_action :update, @group - flash[:notice] = I18n.t('admin.groups.updated_msg') - redirect_to admin_groups_path(page: params[:page], **@filter_params) - end - - private - - def set_group - @group = Group.find(params[:id]) - end - - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys - end - - def resource_params - params.require(:group).permit(:is_featured, :is_nsfw) - end - - def filtered_groups - query = Group.order('is_featured DESC, member_count DESC') - - if params[:title] - query = query.where("LOWER(title) LIKE LOWER(?)", "%#{params[:title]}%") - end - - return query - end - - def filter_params - params.permit(:sort,) - end + class GroupsController < BaseController + before_action :set_group, except: [:index] + before_action :set_filter_params + + def index + authorize :group, :index? + @groups = filtered_groups.page(params[:page]) end + + def show + authorize :group, :index? + end + + def update + # + end + + def destroy + authorize @group, :destroy? + @group.destroy! + log_action :destroy, @group + flash[:notice] = I18n.t('admin.groups.destroyed_msg') + redirect_to admin_groups_path(page: params[:page], **@filter_params) + end + + def enable_featured + authorize @group, :update? + @group.is_featured = true + @group.save! + log_action :update, @group + flash[:notice] = I18n.t('admin.groups.updated_msg') + redirect_to admin_groups_path(page: params[:page], **@filter_params) + end + + def disable_featured + authorize @group, :update? + @group.is_featured = false + @group.save! + log_action :update, @group + flash[:notice] = I18n.t('admin.groups.updated_msg') + redirect_to admin_groups_path(page: params[:page], **@filter_params) + end + + private + + def set_group + @group = Group.find(params[:id]) + end + + def set_filter_params + @filter_params = filter_params.to_hash.symbolize_keys + end + + def resource_params + params.require(:group).permit(:is_featured, :is_nsfw) + end + + def filtered_groups + query = Group.order('is_featured DESC, member_count DESC') + + if params[:title] + query = query.where("LOWER(title) LIKE LOWER(?)", "%#{params[:title]}%") + end + return query + end + + def filter_params + params.permit(:sort,) + end + end end + \ No newline at end of file diff --git a/app/controllers/admin/joined_groups_controller.rb b/app/controllers/admin/joined_groups_controller.rb new file mode 100644 index 00000000..82f08c91 --- /dev/null +++ b/app/controllers/admin/joined_groups_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class JoinedGroupsController < BaseController + before_action :set_account + + PER_PAGE = 25 + + def index + authorize :account, :index? + @groups = @account.groups.page(params[:page]).per(PER_PAGE) + end + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 0fb7d3ca..29ae8b0a 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -12,6 +12,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController def update @account = current_account + # : todo : add link blocking check for bio UpdateAccountService.new.call(@account, account_params, raise_error: true) UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params render json: @account, serializer: REST::CredentialAccountSerializer diff --git a/app/controllers/api/v1/albums_controller.rb b/app/controllers/api/v1/albums_controller.rb new file mode 100644 index 00000000..9376dc63 --- /dev/null +++ b/app/controllers/api/v1/albums_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class Api::V1::AlbumsController < Api::BaseController + before_action :require_user! + before_action :set_albums, only: :index + before_action :set_album, only: [:show, :update, :destroy] + + def index + render json: @albums, each_serializer: REST::AlbumSerializer + end + + def create + @album = "" #current_account.custom_filters.create!(resource_params) + render json: @album, serializer: REST::AlbumSerializer + end + + def show + render json: @album, serializer: REST::AlbumSerializer + end + + def update + @album.update!(resource_params) + render json: @album, serializer: REST::AlbumSerializer + end + + def destroy + @album.destroy! + render_empty_success + end + + private + + def set_albums + @albums = "" #current_account.custom_filters + end + + def set_album + @album = "" # current_account.custom_filters.find(params[:id]) + end + + def resource_params + params.permit(:title, :description, :visibility) + end +end diff --git a/app/controllers/api/v1/bookmark_collections_controller.rb b/app/controllers/api/v1/bookmark_collections_controller.rb new file mode 100644 index 00000000..ea1f77a6 --- /dev/null +++ b/app/controllers/api/v1/bookmark_collections_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class Api::V1::BookmarkCollectionsController < Api::BaseController + before_action :require_user! + before_action :set_bookmark_collections, only: :index + before_action :set_bookmark_collection, only: [:show, :update, :destroy] + + def index + render json: @bookmark_collections, each_serializer: REST::BookmarkCollectionSerializer + end + + def create + @bookmark_collection = "" #current_account.custom_filters.create!(resource_params) + render json: @bookmark_collection, serializer: REST::BookmarkCollectionSerializer + end + + def show + render json: @bookmark_collection, serializer: REST::BookmarkCollectionSerializer + end + + def update + @bookmark_collection.update!(resource_params) + render json: @bookmark_collection, serializer: REST::BookmarkCollectionSerializer + end + + def destroy + @bookmark_collection.destroy! + render_empty_success + end + + private + + def set_bookmark_collections + @bookmark_collections = "" #current_account.custom_filters + end + + def set_bookmark_collection + @bookmark_collection = "" # current_account.custom_filters.find(params[:id]) + end + + def resource_params + params.permit(:title) + 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 d07cd05f..1319f083 100644 --- a/app/controllers/api/v1/chat_conversation_accounts_controller.rb +++ b/app/controllers/api/v1/chat_conversation_accounts_controller.rb @@ -35,6 +35,15 @@ class Api::V1::ChatConversationAccountsController < Api::BaseController render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end + def set_expiration_policy + if current_user.account.is_pro + # + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + else + render json: { error: 'You need to be a GabPRO member to access this' }, status: 422 + end + end + private def set_account diff --git a/app/controllers/api/v1/chat_conversation_controller.rb b/app/controllers/api/v1/chat_conversation_controller.rb index dbb113d1..13d48583 100644 --- a/app/controllers/api/v1/chat_conversation_controller.rb +++ b/app/controllers/api/v1/chat_conversation_controller.rb @@ -6,7 +6,7 @@ class Api::V1::ChatConversationController < Api::BaseController before_action :require_user! before_action :set_account, only: :create - before_action :set_chat_conversation, only: [:show, :mark_chat_conversation_approved, :mark_chat_conversation_hidden, :mark_chat_conversation_unread] + before_action :set_chat_conversation, only: [:show, :mark_chat_conversation_approved, :mark_chat_conversation_hidden, :mark_chat_conversation_read] def show render json: {}, each_serializer: REST::ChatConversationAccountSerializer @@ -23,8 +23,8 @@ class Api::V1::ChatConversationController < Api::BaseController render json: chat_conversation_account, each_serializer: REST::ChatConversationAccountSerializer end - def mark_chat_conversation_unread - @chat_conversation_account.update!(unread_count: 1) + def mark_chat_conversation_read + @chat_conversation_account.update!(unread_count: 0) render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer end @@ -34,8 +34,13 @@ class Api::V1::ChatConversationController < Api::BaseController end def mark_chat_conversation_approved - @chat_conversation_account.update!(is_approved: true) - render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + approved_conversation_count = ChatConversationAccount.where(account: @account, is_hidden: false, is_approved: true).count + if approved_conversation_count >= ChatConversationAccount::PER_ACCOUNT_APPROVED_LIMIT + render json: { error: true, message: "You have #{approved_conversation_count} active chat conversations. The limit is #{ChatConversationAccount::PER_ACCOUNT_APPROVED_LIMIT}. Delete some conversations first before approving any more requests." } + else + @chat_conversation_account.update!(is_approved: true) + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + end end private diff --git a/app/controllers/api/v1/chat_conversations/messages_controller.rb b/app/controllers/api/v1/chat_conversations/messages_controller.rb index e9e6a948..c0e0aa13 100644 --- a/app/controllers/api/v1/chat_conversations/messages_controller.rb +++ b/app/controllers/api/v1/chat_conversations/messages_controller.rb @@ -11,24 +11,16 @@ class Api::V1::ChatConversations::MessagesController < Api::BaseController after_action :insert_pagination_headers, unless: -> { @chats.empty? } def show - puts "tilly chat_message_conversations - 1: " + @chats.count.inspect render json: @chats, each_serializer: REST::ChatMessageSerializer end 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 + if current_user.account.is_pro + @chat_conversation_account = PurgeChatMessagesService.new.call(current_user.account, @chat_conversation) + render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer + else + render json: { error: 'You need to be a GabPRO member to access this' }, status: 422 + end end private diff --git a/app/controllers/api/v1/chat_messages_controller.rb b/app/controllers/api/v1/chat_messages_controller.rb index 438ca0fb..ec6a1b56 100644 --- a/app/controllers/api/v1/chat_messages_controller.rb +++ b/app/controllers/api/v1/chat_messages_controller.rb @@ -5,50 +5,20 @@ class Api::V1::ChatMessagesController < Api::BaseController before_action -> { doorkeeper_authorize! :write, :'write:chats' } before_action :require_user! - 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: ActionController::Base.helpers.strip_tags(params[:text]) - ) - - # : todo : - # check if blocked - # update unread_count++ if offline - - @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 - + @chat_conversation = ChatConversation.find(chat_params[:chat_conversation_id]) + @chat = PostChatMessageService.new.call(current_user.account, text: chat_params[:text], chat_conversation: @chat_conversation) render json: @chat, serializer: REST::ChatMessageSerializer end def destroy - @chat = ChatMessage.where(from_account: current_user.account).find(params[:id]) - - # : todo : - # make sure last_chat_message_id in chat_account_conversation gets set to last - - @chat.destroy! - + @chat = DeleteChatMessageService.new.call(current_user.account, params[:id]) render json: @chat, serializer: REST::ChatMessageSerializer end private - def set_chat_conversation - @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 - @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/controllers/api/v1/groups_controller.rb b/app/controllers/api/v1/groups_controller.rb index c56de41a..c67a8961 100644 --- a/app/controllers/api/v1/groups_controller.rb +++ b/app/controllers/api/v1/groups_controller.rb @@ -46,7 +46,7 @@ class Api::V1::GroupsController < Api::BaseController @groups = [] if !@groupCategory.nil? - @groups = Group.where(is_archived: false, group_categories: @groupCategory).all + @groups = Group.where(is_archived: false, group_categories: @groupCategory).order('member_count DESC').all end render json: @groups, each_serializer: REST::GroupSerializer @@ -59,7 +59,7 @@ class Api::V1::GroupsController < Api::BaseController @groups = [] if !params[:tag].empty? - @groups = Group.where(is_archived: false).where("array_to_string(tags, '||') ILIKE :tag", tag: "%#{params[:tag]}%").all + @groups = Group.where(is_archived: false).where("array_to_string(tags, '||') ILIKE :tag", tag: "%#{params[:tag]}%").order('member_count DESC').all end render json: @groups, each_serializer: REST::GroupSerializer diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb index 28fb06a5..9346f9e3 100644 --- a/app/controllers/api/v1/timelines/home_controller.rb +++ b/app/controllers/api/v1/timelines/home_controller.rb @@ -62,6 +62,6 @@ class Api::V1::Timelines::HomeController < Api::BaseController end def regeneration_in_progress? - Redis.current.exists("account:#{current_account.id}:regeneration") + Redis.current.exists?("account:#{current_account.id}:regeneration") end end diff --git a/app/controllers/empty_controller.rb b/app/controllers/empty_controller.rb index bde3493e..20d28bab 100644 --- a/app/controllers/empty_controller.rb +++ b/app/controllers/empty_controller.rb @@ -48,7 +48,25 @@ class EmptyController < ActionController::Base nil end - protected + def cache_collection(raw, klass) + return raw unless klass.respond_to?(:with_includes) + + raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) + cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id) + uncached_ids = raw.map(&:id) - cached_keys_with_value.keys + + klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!) + + unless uncached_ids.empty? + uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item } + + uncached.each_value do |item| + Rails.cache.write(item, item) + end + end + + raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact + end def limit_param(default_limit) return default_limit unless params[:limit] diff --git a/app/controllers/manifests_controller.rb b/app/controllers/manifests_controller.rb index 09985129..197cac29 100644 --- a/app/controllers/manifests_controller.rb +++ b/app/controllers/manifests_controller.rb @@ -3,7 +3,7 @@ class ManifestsController < EmptyController def show - render json: InstancePresenter.new, serializer: ManifestSerializer + render json:{} # InstancePresenter.new, serializer: ManifestSerializer end end diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 0312017b..3fdf930a 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -20,7 +20,13 @@ class Settings::ProfilesController < Settings::BaseController if @account.is_verified && params[:account][:display_name] && @account.display_name != params[:account][:display_name] flash[:alert] = 'Unable to change Display name for verified account' redirect_to settings_profile_path + elsif !@account.is_pro && params[:account][:username] && @account.username != params[:account][:username] + flash[:alert] = 'Unable to change username for your account. You are not GabPRO' + redirect_to settings_profile_path else + # : todo : + # only allowed to change username once per day + if UpdateAccountService.new.call(@account, account_params) redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg') else @@ -33,7 +39,7 @@ class Settings::ProfilesController < Settings::BaseController private def account_params - params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value]) + params.require(:account).permit(:display_name, :username, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value]) end def set_account diff --git a/app/controllers/settings/promotions_controller.rb b/app/controllers/settings/promotions_controller.rb index 147e6131..0a93a465 100644 --- a/app/controllers/settings/promotions_controller.rb +++ b/app/controllers/settings/promotions_controller.rb @@ -46,11 +46,11 @@ class Settings::PromotionsController < Admin::BaseController @promotion = Promotion.find(params[:id]) end - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys - end + def set_filter_params + @filter_params = filter_params.to_hash.symbolize_keys + end - def resource_params - params.require(:promotion).permit(:expires_at, :status_id, :timeline_id, :position) - end + def resource_params + params.require(:promotion).permit(:expires_at, :status_id, :timeline_id, :position) + end end diff --git a/app/controllers/settings/trending_hashtags_controller.rb b/app/controllers/settings/trending_hashtags_controller.rb new file mode 100644 index 00000000..acd4dcad --- /dev/null +++ b/app/controllers/settings/trending_hashtags_controller.rb @@ -0,0 +1,10 @@ +class Settings::TrendingHashtagsController < Admin::BaseController + def index + @trending_hashtags = Redis.current.get("admin_trending_hashtags") || '' + end + + def create + Redis.current.set("admin_trending_hashtags", params[:trending_hashtags]) + redirect_to settings_trending_hashtags_path + end +end diff --git a/app/controllers/settings/verifications/moderation_controller.rb b/app/controllers/settings/verifications/moderation_controller.rb index 8f893ea7..7bdb8e57 100644 --- a/app/controllers/settings/verifications/moderation_controller.rb +++ b/app/controllers/settings/verifications/moderation_controller.rb @@ -1,10 +1,10 @@ class Settings::Verifications::ModerationController < Admin::BaseController def index - @verification_requests = AccountVerificationRequest.all + @verification_requests = AccountVerificationRequest.order('created_at DESC').all end def approve - verification_request = AccountVerificationRequest.find params[:id] + verification_request = AccountVerificationRequest.find(params[:id]) # Mark user as verified account = verification_request.account @@ -22,6 +22,8 @@ class Settings::Verifications::ModerationController < Admin::BaseController end def reject - @verification_requests = AccountVerificationRequest.find params[:id] + verification_request = AccountVerificationRequest.find(params[:id]) + verification_request.destroy() + redirect_to settings_verifications_moderation_url, notice: I18n.t('verifications.moderation.rejected_msg') end end diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 948a9be0..442586fc 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -38,12 +38,6 @@ module StatusesHelper content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976') end - def account_badge(account, all: false) - if account.bot? - content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles') - end - end - def link_to_more(url) link_to t('statuses.show_more'), url, class: 'load-more load-gap' end diff --git a/app/javascript/gabsocial/actions/albums.js b/app/javascript/gabsocial/actions/albums.js new file mode 100644 index 00000000..ab0c0141 --- /dev/null +++ b/app/javascript/gabsocial/actions/albums.js @@ -0,0 +1 @@ +// \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/bookmarks.js b/app/javascript/gabsocial/actions/bookmarks.js index 26007292..5f77c9e4 100644 --- a/app/javascript/gabsocial/actions/bookmarks.js +++ b/app/javascript/gabsocial/actions/bookmarks.js @@ -10,6 +10,20 @@ export const BOOKMARKED_STATUSES_EXPAND_REQUEST = 'BOOKMARKED_STATUSES_EXPAND_RE export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS' export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL' +// + +export const BOOKMARK_COLLECTIONS_FETCH_REQUEST = 'BOOKMARK_COLLECTIONS_FETCH_REQUEST' +export const BOOKMARK_COLLECTIONS_FETCH_SUCCESS = 'BOOKMARK_COLLECTIONS_FETCH_SUCCESS' +export const BOOKMARK_COLLECTIONS_FETCH_FAIL = 'BOOKMARK_COLLECTIONS_FETCH_FAIL' + +export const BOOKMARK_COLLECTIONS_CREATE_REQUEST = 'BOOKMARK_COLLECTIONS_CREATE_REQUEST' +export const BOOKMARK_COLLECTIONS_CREATE_SUCCESS = 'BOOKMARK_COLLECTIONS_CREATE_SUCCESS' +export const BOOKMARK_COLLECTIONS_CREATE_FAIL = 'BOOKMARK_COLLECTIONS_CREATE_FAIL' + +export const BOOKMARK_COLLECTIONS_REMOVE_REQUEST = 'BOOKMARK_COLLECTIONS_REMOVE_REQUEST' +export const BOOKMARK_COLLECTIONS_REMOVE_SUCCESS = 'BOOKMARK_COLLECTIONS_REMOVE_SUCCESS' +export const BOOKMARK_COLLECTIONS_REMOVE_FAIL = 'BOOKMARK_COLLECTIONS_REMOVE_FAIL' + /** * */ @@ -22,11 +36,11 @@ export const fetchBookmarkedStatuses = () => (dispatch, getState) => { dispatch(fetchBookmarkedStatusesRequest()) - api(getState).get('/api/v1/bookmarks').then(response => { + 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)) - }).catch(error => { + }).catch((error) => { dispatch(fetchBookmarkedStatusesFail(error)) }) } @@ -61,11 +75,11 @@ export const expandBookmarkedStatuses = () => (dispatch, getState) => { dispatch(expandBookmarkedStatusesRequest()) - api(getState).get(url).then(response => { + 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)) - }).catch(error => { + }).catch((error) => { dispatch(expandBookmarkedStatusesFail(error)) }) } @@ -85,3 +99,95 @@ const expandBookmarkedStatusesFail = (error) => ({ showToast: true, error, }) + +/** + * + */ +export const fetchBookmarkCollections = () => (dispatch, getState) => { + if (!me) return + + if (getState().getIn(['bookmark_collections', 'isLoading'])) return + + dispatch(fetchBookmarkCollectionsRequest()) + + api(getState).get('/api/v1/bookmark_collections').then((response) => { + dispatch(fetchBookmarkCollectionsSuccess(response.data)) + }).catch((error) => { + dispatch(fetchBookmarkCollectionsFail(error)) + }) +} + +const fetchBookmarkCollectionsRequest = () => ({ + type: BOOKMARK_COLLECTIONS_FETCH_REQUEST, +}) + +const fetchBookmarkCollectionsSuccess = (bookmarkCollections) => ({ + type: BOOKMARK_COLLECTIONS_FETCH_SUCCESS, + bookmarkCollections, +}) + +const fetchBookmarkCollectionsFail = (error) => ({ + type: BOOKMARK_COLLECTIONS_FETCH_FAIL, + showToast: true, + error, +}) + +/** + * + */ +export const createBookmarkCollection = (title) => (dispatch, getState) => { + if (!me || !title) return + + dispatch(createBookmarkCollectionRequest()) + + api(getState).post('/api/v1/bookmark_collections', { title }).then((response) => { + dispatch(createBookmarkCollectionSuccess(response.data)) + }).catch((error) => { + dispatch(createBookmarkCollectionFail(error)) + }) +} + +const createBookmarkCollectionRequest = () => ({ + type: BOOKMARK_COLLECTIONS_CREATE_REQUEST, +}) + +const createBookmarkCollectionSuccess = (bookmarkCollection) => ({ + type: BOOKMARK_COLLECTIONS_CREATE_SUCCESS, + bookmarkCollection, +}) + +const createBookmarkCollectionFail = (error) => ({ + type: BOOKMARK_COLLECTIONS_CREATE_FAIL, + showToast: true, + error, +}) + +/** + * + */ +export const removeBookmarkCollection = (bookmarkCollectionId) => (dispatch, getState) => { + if (!me || !bookmarkCollectionId) return + + dispatch(removeBookmarkCollectionRequest(bookmarkCollectionId)) + + api(getState).delete(`/api/v1/bookmark_collection/${bookmarkCollectionId}`).then((response) => { + dispatch(removeBookmarkCollectionSuccess(response.data)) + }).catch((error) => { + dispatch(removeBookmarkCollectionFail(error)) + }) +} + +const removeBookmarkCollectionRequest = (bookmarkCollectionId) => ({ + type: BOOKMARK_COLLECTIONS_CREATE_REQUEST, + bookmarkCollectionId, +}) + +const removeBookmarkCollectionSuccess = () => ({ + type: BOOKMARK_COLLECTIONS_CREATE_SUCCESS, +}) + +const removeBookmarkCollectionFail = (error) => ({ + type: BOOKMARK_COLLECTIONS_CREATE_FAIL, + showToast: true, + error, +}) \ No newline at end of file diff --git a/app/javascript/gabsocial/actions/chat_conversation_accounts.js b/app/javascript/gabsocial/actions/chat_conversation_accounts.js index c4cdb920..778fbd3f 100644 --- a/app/javascript/gabsocial/actions/chat_conversation_accounts.js +++ b/app/javascript/gabsocial/actions/chat_conversation_accounts.js @@ -46,6 +46,7 @@ export const IS_CHAT_MESSENGER_MUTED_SUCCESS = 'IS_CHAT_MESSENGER_MUTED_SUCCESS' * */ export const blockChatMessenger = (accountId) => (dispatch, getState) => { + console.log("blockChatMessenger:", accountId) if (!me || !accountId) return dispatch(blockChatMessengerRequest(accountId)) diff --git a/app/javascript/gabsocial/actions/chat_conversation_messages.js b/app/javascript/gabsocial/actions/chat_conversation_messages.js index d59892f3..06acb621 100644 --- a/app/javascript/gabsocial/actions/chat_conversation_messages.js +++ b/app/javascript/gabsocial/actions/chat_conversation_messages.js @@ -44,10 +44,10 @@ export const clearChatMessageConversation = (chatConversationId) => (dispatch) = /** * */ -export const scrollBottomChatMessageConversation = (chatConversationId, top) => ({ +export const scrollBottomChatMessageConversation = (chatConversationId, bottom) => ({ type: CHAT_CONVERSATION_MESSAGES_SCROLL_BOTTOM, chatConversationId, - top, + bottom, }) /** @@ -56,7 +56,7 @@ export const scrollBottomChatMessageConversation = (chatConversationId, top) => export const expandChatMessages = (chatConversationId, params = {}, done = noop) => (dispatch, getState) => { if (!me || !chatConversationId) return - const chatConversation = getState().getIn(['chat_messages', chatConversationId], ImmutableMap()) + const chatConversation = getState().getIn(['chat_conversations', chatConversationId], ImmutableMap()) const isLoadingMore = !!params.maxId if (!!chatConversation && (chatConversation.get('isLoading') || chatConversation.get('isError'))) { diff --git a/app/javascript/gabsocial/actions/chat_conversations.js b/app/javascript/gabsocial/actions/chat_conversations.js index f77c230f..36ac51a1 100644 --- a/app/javascript/gabsocial/actions/chat_conversations.js +++ b/app/javascript/gabsocial/actions/chat_conversations.js @@ -45,6 +45,22 @@ export const CHAT_CONVERSATION_DELETE_REQUEST = 'CHAT_CONVERSATION_DELETE_REQUES export const CHAT_CONVERSATION_DELETE_SUCCESS = 'CHAT_CONVERSATION_DELETE_SUCCESS' export const CHAT_CONVERSATION_DELETE_FAIL = 'CHAT_CONVERSATION_DELETE_FAIL' +// + +export const CHAT_CONVERSATION_MARK_READ_FETCH = 'CHAT_CONVERSATION_MARK_READ_FETCH' +export const CHAT_CONVERSATION_MARK_READ_SUCCESS = 'CHAT_CONVERSATION_MARK_READ_SUCCESS' +export const CHAT_CONVERSATION_MARK_READ_FAIL = 'CHAT_CONVERSATION_MARK_READ_FAIL' + +export const CHAT_CONVERSATION_HIDE_FETCH = 'CHAT_CONVERSATION_HIDE_FETCH' +export const CHAT_CONVERSATION_HIDE_SUCCESS = 'CHAT_CONVERSATION_HIDE_SUCCESS' +export const CHAT_CONVERSATION_HIDE_FAIL = 'CHAT_CONVERSATION_HIDE_FAIL' + +// + +export const SET_CHAT_CONVERSATION_EXPIRATION_REQUEST = 'SET_CHAT_CONVERSATION_EXPIRATION_REQUEST' +export const SET_CHAT_CONVERSATION_EXPIRATION_SUCCESS = 'SET_CHAT_CONVERSATION_EXPIRATION_SUCCESS' +export const SET_CHAT_CONVERSATION_EXPIRATION_FAIL = 'SET_CHAT_CONVERSATION_EXPIRATION_FAIL' + /** * @description Fetch paginated active chat conversations, import accounts and set chat converations */ @@ -309,4 +325,93 @@ export const approveChatConversationRequestSuccess = (chatConversation) => ({ export const approveChatConversationRequestFail = () => ({ type: CHAT_CONVERSATION_REQUEST_APPROVE_FAIL, -}) \ No newline at end of file +}) + +/** + * + */ +export const hideChatConversation = (chatConversationId) => (dispatch, getState) => { + if (!me|| !chatConversationId) return + + dispatch(hideChatConversationFetch(chatConversationId)) + + api(getState).post(`/api/v1/chat_conversation/${chatConversationId}/mark_chat_conversation_hidden`).then((response) => { + dispatch(approveChatConversationRequestSuccess(chatConversationId)) + }).catch((error) => dispatch(approveChatConversationRequestFail(error))) +} + +export const hideChatConversationFetch = (chatConversationId) => ({ + type: CHAT_CONVERSATION_HIDE_SUCCESS, + chatConversationId, +}) + +export const hideChatConversationSuccess = (chatConversationId) => ({ + type: CHAT_CONVERSATION_HIDE_SUCCESS, + chatConversationId, +}) + +export const hideChatConversationFail = () => ({ + type: CHAT_CONVERSATION_HIDE_FAIL, +}) + +/** + * + */ +export const readChatConversation = (chatConversationId) => (dispatch, getState) => { + if (!me|| !chatConversationId) return + + const chatConversation = getState().getIn(['chat_conversations', chatConversationId]) + if (!chatConversation) return + if (chatConversation.get('unread_count') < 1) return + + dispatch(readChatConversationFetch(chatConversation)) + + api(getState).post(`/api/v1/chat_conversation/${chatConversationId}/mark_chat_conversation_read`).then((response) => { + dispatch(readChatConversationSuccess(response.data)) + }).catch((error) => dispatch(readChatConversationFail(error))) +} + +export const readChatConversationFetch = (chatConversation) => ({ + type: CHAT_CONVERSATION_MARK_READ_FETCH, + chatConversation, +}) + +export const readChatConversationSuccess = (chatConversation) => ({ + type: CHAT_CONVERSATION_MARK_READ_SUCCESS, + chatConversation, +}) + +export const readChatConversationFail = () => ({ + type: CHAT_CONVERSATION_MARK_READ_FAIL, +}) + +/** + * + */ +export const setChatConversationExpiration = (chatConversationId, expiration) => (dispatch, getState) => { + if (!me|| !chatConversationId || !expiration) return + + dispatch(setChatConversationExpirationFetch(chatConversation)) + + api(getState).post(`/api/v1/chat_conversation/${chatConversationId}/set_expiration_policy`, { + expiration, + }).then((response) => { + dispatch(setChatConversationExpirationSuccess(response.data)) + }).catch((error) => dispatch(setChatConversationExpirationFail(error))) +} + +export const setChatConversationExpirationFetch = (chatConversation) => ({ + type: SET_CHAT_CONVERSATION_EXPIRATION_REQUEST, + chatConversation, +}) + +export const setChatConversationExpirationSuccess = (chatConversation) => ({ + type: SET_CHAT_CONVERSATION_EXPIRATION_REQUEST, + chatConversation, +}) + +export const setChatConversationExpirationFail = (error) => ({ + type: SET_CHAT_CONVERSATION_EXPIRATION_REQUEST, + error, +}) + diff --git a/app/javascript/gabsocial/actions/chat_messages.js b/app/javascript/gabsocial/actions/chat_messages.js index 4b69eec4..3cb046ad 100644 --- a/app/javascript/gabsocial/actions/chat_messages.js +++ b/app/javascript/gabsocial/actions/chat_messages.js @@ -56,6 +56,22 @@ const sendChatMessageFail = (error) => ({ error, }) +/** + * + */ +export const manageIncomingChatMessage = (chatMessage) => (dispatch, getState) => { + if (!chatMessage) return + + console.log("chatMessage:", chatMessage) + dispatch(sendChatMessageSuccess(chatMessage)) + + const isOnline = getState().getIn(['chat_conversation_messages', chatMessage.chat_conversation_id, 'online']) + console.log("isOnline: ", isOnline) + + // : todo : + // Check if is online for conversation, if not increase total/convo unread count +} + /** * */ @@ -99,24 +115,25 @@ export const purgeChatMessages = (chatConversationId) => (dispatch, getState) => dispatch(deleteChatMessagesRequest(chatConversationId)) - api(getState).delete(`/api/v1/chat_conversations/${chatConversationId}/messages/destroy_all`).then((response) => { + api(getState).delete(`/api/v1/chat_conversations/messages/${chatConversationId}/destroy_all`).then((response) => { dispatch(deleteChatMessagesSuccess(response.data)) }).catch((error) => { dispatch(deleteChatMessagesFail(error)) }) } -const deleteChatMessagesRequest = () => ({ +const purgeChatMessagesRequest = (chatConversationId) => ({ type: CHAT_MESSAGES_PURGE_REQUEST, + chatConversationId, }) -const deleteChatMessagesSuccess = (chatConversationId) => ({ +const purgeChatMessagesSuccess = (chatConversationId) => ({ type: CHAT_MESSAGES_PURGE_SUCCESS, chatConversationId, showToast: true, }) -const deleteChatMessagesFail = (error) => ({ +const purgeChatMessagesFail = (error) => ({ type: CHAT_MESSAGES_PURGE_FAIL, showToast: true, error, diff --git a/app/javascript/gabsocial/actions/compose.js b/app/javascript/gabsocial/actions/compose.js index f3be35d8..22ad2633 100644 --- a/app/javascript/gabsocial/actions/compose.js +++ b/app/javascript/gabsocial/actions/compose.js @@ -20,12 +20,12 @@ import { defineMessages } from 'react-intl' import { openModal, closeModal } from './modal' import { MODAL_COMPOSE, - STATUS_EXPIRATION_OPTION_5_MINUTES, - STATUS_EXPIRATION_OPTION_60_MINUTES, - STATUS_EXPIRATION_OPTION_6_HOURS, - STATUS_EXPIRATION_OPTION_24_HOURS, - STATUS_EXPIRATION_OPTION_3_DAYS, - STATUS_EXPIRATION_OPTION_7_DAYS, + EXPIRATION_OPTION_5_MINUTES, + EXPIRATION_OPTION_60_MINUTES, + EXPIRATION_OPTION_6_HOURS, + EXPIRATION_OPTION_24_HOURS, + EXPIRATION_OPTION_3_DAYS, + EXPIRATION_OPTION_7_DAYS, } from '../constants' import { me } from '../initial_state' import { makeGetStatus } from '../selectors' @@ -347,17 +347,17 @@ export const submitCompose = (groupId, replyToId = null, router, isStandalone, a let expires_at = getState().getIn(['compose', 'expires_at'], null) if (expires_at) { - if (expires_at === STATUS_EXPIRATION_OPTION_5_MINUTES) { + if (expires_at === EXPIRATION_OPTION_5_MINUTES) { expires_at = moment.utc().add('5', 'minute').toDate() - } else if (expires_at === STATUS_EXPIRATION_OPTION_60_MINUTES) { + } else if (expires_at === EXPIRATION_OPTION_60_MINUTES) { expires_at = moment.utc().add('60', 'minute').toDate() - } else if (expires_at === STATUS_EXPIRATION_OPTION_6_HOURS) { + } else if (expires_at === EXPIRATION_OPTION_6_HOURS) { expires_at = moment.utc().add('6', 'hour').toDate() - } else if (expires_at === STATUS_EXPIRATION_OPTION_24_HOURS) { + } else if (expires_at === EXPIRATION_OPTION_24_HOURS) { expires_at = moment.utc().add('24', 'hour').toDate() - } else if (expires_at === STATUS_EXPIRATION_OPTION_3_DAYS) { + } else if (expires_at === EXPIRATION_OPTION_3_DAYS) { expires_at = moment.utc().add('3', 'day').toDate() - } else if (expires_at === STATUS_EXPIRATION_OPTION_7_DAYS) { + } else if (expires_at === EXPIRATION_OPTION_7_DAYS) { expires_at = moment.utc().add('7', 'day').toDate() } } diff --git a/app/javascript/gabsocial/actions/streaming.js b/app/javascript/gabsocial/actions/streaming.js index 6ef58d25..578ce57e 100644 --- a/app/javascript/gabsocial/actions/streaming.js +++ b/app/javascript/gabsocial/actions/streaming.js @@ -6,7 +6,7 @@ import { updateTimelineQueue, } from './timelines' import { updateNotificationsQueue } from './notifications' -import { sendChatMessageSuccess } from './chat_messages' +import { manageIncomingChatMessage } from './chat_messages' import { fetchFilters } from './filters' import { getLocale } from '../locales' import { handleComposeSubmit } from './compose' @@ -84,7 +84,7 @@ export const connectChatMessagesStream = (accountId) => { onReceive (data) { if (!data['event'] || !data['payload']) return if (data.event === 'notification') { - dispatch(sendChatMessageSuccess(JSON.parse(data.payload))) + dispatch(manageIncomingChatMessage(JSON.parse(data.payload))) } }, } diff --git a/app/javascript/gabsocial/components/album.js b/app/javascript/gabsocial/components/album.js new file mode 100644 index 00000000..e69de29b diff --git a/app/javascript/gabsocial/components/autosuggest_textbox.js b/app/javascript/gabsocial/components/autosuggest_textbox.js index 96f6fac7..cd682195 100644 --- a/app/javascript/gabsocial/components/autosuggest_textbox.js +++ b/app/javascript/gabsocial/components/autosuggest_textbox.js @@ -189,6 +189,7 @@ class AutosuggestTextbox extends ImmutablePureComponent { id, isPro, isEdit, + isModalOpen, } = this.props const { suggestionsHidden } = this.state diff --git a/app/javascript/gabsocial/components/link_footer.js b/app/javascript/gabsocial/components/link_footer.js index 957c7c0d..b71d3334 100644 --- a/app/javascript/gabsocial/components/link_footer.js +++ b/app/javascript/gabsocial/components/link_footer.js @@ -1,29 +1,24 @@ import React from 'react' import PropTypes from 'prop-types' -import { connect } from 'react-redux' import { FormattedMessage, defineMessages, injectIntl, } from 'react-intl' -import { openModal } from '../actions/modal' import { + me, repository, source_url, - me, } from '../initial_state' -import { CX, DEFAULT_REL } from '../constants' +import { DEFAULT_REL } from '../constants' import Text from './text' import Button from './button' +import DotTextSeperator from './dot_text_seperator' class LinkFooter extends React.PureComponent { render() { - const { - intl, - noPadding, - onOpenHotkeys, - } = this.props + const { intl } = this.props const currentYear = new Date().getFullYear() @@ -32,12 +27,6 @@ class LinkFooter extends React.PureComponent { href: 'https://help.gab.com', text: intl.formatMessage(messages.help), }, - // : todo : - // { - // onClick: onOpenHotkeys, - // text: intl.formatMessage(messages.hotkeys), - // requiresUser: true, - // }, { href: '/auth/edit', text: intl.formatMessage(messages.security), @@ -52,16 +41,16 @@ class LinkFooter extends React.PureComponent { text: intl.formatMessage(messages.investors), }, { - to: '/about/tos', - text: intl.formatMessage(messages.terms), + to: '/about/sales', + text: intl.formatMessage(messages.salesTerms), }, { to: '/about/dmca', text: intl.formatMessage(messages.dmca), }, { - to: '/about/sales', - text: intl.formatMessage(messages.salesTerms), + to: '/about/tos', + text: intl.formatMessage(messages.terms), }, { to: '/about/privacy', @@ -75,36 +64,33 @@ class LinkFooter extends React.PureComponent { }, ] - const containerClasses = CX({ - d: 1, - px10: !noPadding, - mb15: 1, - }) - return ( -
+