diff --git a/.ruby-version b/.ruby-version index 4d9d11cf50..6cb9d3dd0d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.2 +3.4.3 diff --git a/Gemfile.lock b/Gemfile.lock index 1ed4b71318..f13df0c43f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -94,7 +94,7 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.3.2) - aws-partitions (1.1080.0) + aws-partitions (1.1087.0) aws-sdk-core (3.215.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -120,7 +120,7 @@ GEM rack (>= 0.9.0) rouge (>= 1.0.0) bigdecimal (3.1.9) - bindata (2.5.0) + bindata (2.5.1) binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) blurhash (0.1.8) @@ -170,7 +170,7 @@ GEM crass (1.0.6) css_parser (1.21.1) addressable - csv (3.3.3) + csv (3.3.4) database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) @@ -227,7 +227,7 @@ GEM fabrication (2.31.0) faker (3.5.1) i18n (>= 1.8.11, < 2) - faraday (2.12.2) + faraday (2.13.0) faraday-net_http (>= 2.0, < 3.5) json logger @@ -239,7 +239,7 @@ GEM net-http (>= 0.5.0) fast_blank (1.0.1) fastimage (2.4.0) - ffi (1.17.1) + ffi (1.17.2) ffi-compiler (1.3.2) ffi (>= 1.15.5) rake @@ -426,7 +426,7 @@ GEM mime-types (3.6.2) logger mime-types-data (~> 3.2015) - mime-types-data (3.2025.0402) + mime-types-data (3.2025.0408) mini_mime (1.1.5) mini_portile2 (2.8.8) minitest (5.25.5) @@ -585,8 +585,8 @@ GEM ostruct (0.6.1) ox (2.14.22) bigdecimal (>= 3.0) - parallel (1.26.3) - parser (3.3.7.4) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) racc parslet (2.0.0) @@ -751,7 +751,7 @@ GEM rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.44.0) + rubocop-ast (1.44.1) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-capybara (2.22.1) @@ -1085,4 +1085,4 @@ RUBY VERSION ruby 3.4.1p0 BUNDLED WITH - 2.6.7 + 2.6.8 diff --git a/app/controllers/api/v1/accounts/endorsements_controller.rb b/app/controllers/api/v1/accounts/endorsements_controller.rb new file mode 100644 index 0000000000..1e21994a90 --- /dev/null +++ b/app/controllers/api/v1/accounts/endorsements_controller.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::EndorsementsController < Api::BaseController + include Authorization + + before_action -> { authorize_if_got_token! :read, :'read:accounts' }, only: :index + before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index + before_action :require_user!, except: :index + before_action :set_account + before_action :set_endorsed_accounts, only: :index + after_action :insert_pagination_headers, only: :index + + def index + cache_if_unauthenticated! + render json: @endorsed_accounts, each_serializer: REST::AccountSerializer + end + + def create + AccountPin.find_or_create_by!(account: current_account, target_account: @account) + render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter + end + + def destroy + pin = AccountPin.find_by(account: current_account, target_account: @account) + pin&.destroy! + render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter + end + + private + + def set_account + @account = Account.find(params[:account_id]) + end + + def set_endorsed_accounts + @endorsed_accounts = @account.unavailable? ? [] : paginated_endorsed_accounts + end + + def paginated_endorsed_accounts + @account.endorsed_accounts.without_suspended.includes(:account_stat, :user).paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def relationships_presenter + AccountRelationshipsPresenter.new([@account], current_user.account_id) + end + + def next_path + api_v1_account_endorsements_url pagination_params(max_id: pagination_max_id) if records_continue? + end + + def prev_path + api_v1_account_endorsements_url pagination_params(since_id: pagination_since_id) unless @endorsed_accounts.empty? + end + + def pagination_collection + @endorsed_accounts + end + + def records_continue? + @endorsed_accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + end +end diff --git a/app/controllers/api/v1/accounts/featured_tags_controller.rb b/app/controllers/api/v1/accounts/featured_tags_controller.rb index 0101fb469b..f95846366c 100644 --- a/app/controllers/api/v1/accounts/featured_tags_controller.rb +++ b/app/controllers/api/v1/accounts/featured_tags_controller.rb @@ -17,6 +17,6 @@ class Api::V1::Accounts::FeaturedTagsController < Api::BaseController end def set_featured_tags - @featured_tags = @account.suspended? ? [] : @account.featured_tags + @featured_tags = @account.unavailable? ? [] : @account.featured_tags end end diff --git a/app/controllers/api/v1/accounts/pins_controller.rb b/app/controllers/api/v1/accounts/pins_controller.rb deleted file mode 100644 index 0eb13c048c..0000000000 --- a/app/controllers/api/v1/accounts/pins_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::Accounts::PinsController < Api::BaseController - include Authorization - - before_action -> { doorkeeper_authorize! :write, :'write:accounts' } - before_action :require_user! - before_action :set_account - - def create - AccountPin.find_or_create_by!(account: current_account, target_account: @account) - render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter - end - - def destroy - pin = AccountPin.find_by(account: current_account, target_account: @account) - pin&.destroy! - render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter - end - - private - - def set_account - @account = Account.find(params[:account_id]) - end - - def relationships_presenter - AccountRelationshipsPresenter.new([@account], current_user.account_id) - end -end diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb index 4bbbed2673..6bb884faa0 100644 --- a/app/controllers/api/v1/lists_controller.rb +++ b/app/controllers/api/v1/lists_controller.rb @@ -7,10 +7,6 @@ class Api::V1::ListsController < Api::BaseController before_action :require_user! before_action :set_list, except: [:index, :create] - rescue_from ArgumentError do |e| - render json: { error: e.to_s }, status: 422 - end - def index @lists = List.where(account: current_account).all render json: @lists, each_serializer: REST::ListSerializer diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.tsx b/app/javascript/flavours/glitch/components/dropdown_menu.tsx index 91f040ab66..64745ba8c9 100644 --- a/app/javascript/flavours/glitch/components/dropdown_menu.tsx +++ b/app/javascript/flavours/glitch/components/dropdown_menu.tsx @@ -71,6 +71,8 @@ type RenderItemFn = ( }, ) => React.ReactNode; +type ItemClickFn = (item: Item, index: number) => void; + type RenderHeaderFn = (items: Item[]) => React.ReactNode; interface DropdownMenuProps { @@ -81,10 +83,10 @@ interface DropdownMenuProps { openedViaKeyboard: boolean; renderItem?: RenderItemFn; renderHeader?: RenderHeaderFn; - onItemClick: (e: React.MouseEvent | React.KeyboardEvent) => void; + onItemClick?: ItemClickFn; } -const DropdownMenu = ({ +export const DropdownMenu = ({ items, loading, scrollable, @@ -176,20 +178,35 @@ const DropdownMenu = ({ [], ); + const handleItemClick = useCallback( + (e: React.MouseEvent | React.KeyboardEvent) => { + const i = Number(e.currentTarget.getAttribute('data-index')); + const item = items?.[i]; + + onClose(); + + if (!item) { + return; + } + + if (typeof onItemClick === 'function') { + e.preventDefault(); + onItemClick(item, i); + } else if (isActionItem(item)) { + e.preventDefault(); + item.action(); + } + }, + [onClose, onItemClick, items], + ); + const handleItemKeyUp = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { - onItemClick(e); + handleItemClick(e); } }, - [onItemClick], - ); - - const handleClick = useCallback( - (e: React.MouseEvent | React.KeyboardEvent) => { - onItemClick(e); - }, - [onItemClick], + [handleItemClick], ); const nativeRenderItem = (option: Item, i: number) => { @@ -209,7 +226,7 @@ const DropdownMenu = ({ element = (