Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e7cd5a4
Refactor carousel components (#36425)
ChaosExAnima Oct 29, 2025
ebf5cee
Fix media URL inconsistency when deleting statuses via API (#35880)
dbarabashh Oct 29, 2025
a77038b
New Crowdin Translations (automated) (#36641)
github-actions[bot] Oct 30, 2025
2c5d3f9
chore(deps): update dependency oj to v3.16.12 (#36644)
renovate[bot] Oct 30, 2025
ff1e19a
Silence Storybook build warning (#36647)
ChaosExAnima Oct 30, 2025
e5e9f8d
Fix og:images from The Guardian (and possibly other CDNs that check U…
phocks Oct 30, 2025
762e87b
Fix SMTP configuration with `mail` 2.9.0 (#36646)
ClearlyClaire Oct 30, 2025
f3d9a4e
Add CSS Module support (#36637)
ChaosExAnima Oct 30, 2025
28cb345
Fix: Ensure carousel focuses on wrapper (#36649)
ChaosExAnima Oct 30, 2025
13a070f
chore(deps): update dependency webmock to v3.26.1 (#36648)
renovate[bot] Oct 30, 2025
aefd728
Use `before_action` to protect hidden collections in following/follow…
mjankowski Oct 30, 2025
fcecbf3
Show error when submitting empty post rather than failing silently (#…
diondiondion Oct 30, 2025
7b61ad9
Update playwright-ruby-client to version 1.56.0 (#36655)
mjankowski Oct 31, 2025
499ddfe
Add separate translation key for "About this server" string (#36664)
diondiondion Oct 31, 2025
d47ca1c
New Crowdin Translations (automated) (#36660)
github-actions[bot] Oct 31, 2025
2c4367b
chore(deps): update dependency rubocop to v1.81.7 (#36662)
renovate[bot] Oct 31, 2025
fd4e51b
chore(deps): update dependency libvips to v8.17.3 (#36654)
renovate[bot] Oct 31, 2025
35abaa7
chore(deps): update dependency axios to v1.13.1 (#36633)
renovate[bot] Oct 31, 2025
d865a09
Update eslint-plugin-jsdoc to version 61.1.11 (#36653)
mjankowski Oct 31, 2025
8a28266
Bump version to v4.6.0-alpha.1 (#36667)
ClearlyClaire Oct 31, 2025
055f581
Fix initially selected language in Rules panel, hide selector when no…
diondiondion Oct 31, 2025
5fe74d2
chore(deps): update dependency rubyzip to v3.2.2 (#36687)
renovate[bot] Nov 3, 2025
5c0c772
chore(deps): update node.js to 24.11 (#36630)
renovate[bot] Nov 3, 2025
0bf974a
chore(deps): update dependency haml_lint to v0.67.0 (#36645)
renovate[bot] Nov 3, 2025
7faf2ea
chore(deps): update dependency jsdom to v27.1.0 (#36663)
renovate[bot] Nov 3, 2025
8781abf
chore(deps): update dependency sass to v1.93.3 (#36674)
renovate[bot] Nov 3, 2025
f10c79c
chore(deps): update dependency irb to v1.15.3 (#36682)
renovate[bot] Nov 3, 2025
0f01dde
Merge remote-tracking branch 'upstream/main' into update/imastodon
takayamaki Nov 3, 2025
61c0daf
New Crowdin Translations (automated) (#36676)
github-actions[bot] Nov 3, 2025
bae5877
Disable paste-link-to-quote flow when composing Private Mentions (#36…
ClearlyClaire Nov 3, 2025
e79e42f
Fix issuance of quote approval for remote private statuses (#36693)
ClearlyClaire Nov 3, 2025
9b3e92b
Prevent creation of Private Mentions quoting someone who is not menti…
ClearlyClaire Nov 3, 2025
aa8f487
Merge remote-tracking branch 'upstream/main' into update/imastodon
takayamaki Nov 3, 2025
d1f5782
Move "Privacy and reach" from "Public profile" to top-level navigatio…
ChaelCodes Nov 3, 2025
8e2c332
Merge remote-tracking branch 'upstream/main' into update/imastodon
takayamaki Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24.10
24.11
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ group :test do
# Browser integration testing
gem 'capybara', '~> 3.39'
gem 'capybara-playwright-driver'
gem 'playwright-ruby-client', '1.55.0', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package
gem 'playwright-ruby-client', '1.56.0', require: false # Pinning the exact version as it needs to be kept in sync with the installed npm package

# Used to reset the database between system tests
gem 'database_cleaner-active_record'
Expand Down
28 changes: 14 additions & 14 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ GEM
mail (~> 2.7)
email_validator (2.2.4)
activemodel
erb (5.1.1)
erb (5.1.3)
erubi (1.13.1)
et-orbi (1.4.0)
tzinfo
Expand Down Expand Up @@ -282,7 +282,7 @@ GEM
rake (>= 13)
googleapis-common-protos-types (1.22.0)
google-protobuf (~> 4.26)
haml (6.3.0)
haml (6.4.0)
temple (>= 0.8.2)
thor
tilt
Expand All @@ -291,7 +291,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.66.0)
haml_lint (0.67.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
Expand Down Expand Up @@ -340,7 +340,7 @@ GEM
activesupport (>= 3.0)
nokogiri (>= 1.6)
io-console (0.8.1)
irb (1.15.2)
irb (1.15.3)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
Expand All @@ -349,7 +349,7 @@ GEM
azure-blob (~> 0.5.2)
hashie (~> 5.0)
jmespath (1.6.2)
json (2.15.1)
json (2.15.2)
json-canonicalization (1.0.0)
json-jwt (1.17.0)
activesupport (>= 4.2)
Expand Down Expand Up @@ -468,7 +468,7 @@ GEM
nokogiri (1.18.10)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oj (3.16.11)
oj (3.16.12)
bigdecimal (>= 3.0)
ostruct (>= 0.2)
omniauth (2.1.4)
Expand Down Expand Up @@ -584,7 +584,7 @@ GEM
ox (2.14.23)
bigdecimal (>= 3.0)
parallel (1.27.0)
parser (3.3.9.0)
parser (3.3.10.0)
ast (~> 2.4.1)
racc
parslet (2.0.0)
Expand All @@ -593,7 +593,7 @@ GEM
pg (1.6.2)
pghero (3.7.0)
activerecord (>= 7.1)
playwright-ruby-client (1.55.0)
playwright-ruby-client (1.56.0)
concurrent-ruby (>= 1.1.6)
mime-types (>= 3.0)
pp (0.6.3)
Expand All @@ -607,7 +607,7 @@ GEM
net-smtp
premailer (~> 1.7, >= 1.7.9)
prettyprint (0.2.0)
prism (1.5.2)
prism (1.6.0)
prometheus_exporter (2.3.0)
webrick
propshaft (1.3.1)
Expand Down Expand Up @@ -694,7 +694,7 @@ GEM
readline (~> 0.0)
rdf-normalize (0.7.0)
rdf (~> 3.3)
rdoc (6.15.0)
rdoc (6.15.1)
erb
psych (>= 4.0.0)
tsort
Expand Down Expand Up @@ -748,7 +748,7 @@ GEM
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 9)
rspec-support (3.13.6)
rubocop (1.81.6)
rubocop (1.81.7)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
Expand Down Expand Up @@ -794,7 +794,7 @@ GEM
ruby-vips (2.2.5)
ffi (~> 1.12)
logger
rubyzip (3.2.1)
rubyzip (3.2.2)
rufus-scheduler (3.9.2)
fugit (~> 1.1, >= 1.11.1)
safety_net_attestation (0.5.0)
Expand Down Expand Up @@ -914,7 +914,7 @@ GEM
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webmock (3.26.0)
webmock (3.26.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
Expand Down Expand Up @@ -1032,7 +1032,7 @@ DEPENDENCIES
parslet
pg (~> 1.5)
pghero
playwright-ruby-client (= 1.55.0)
playwright-ruby-client (= 1.56.0)
premailer-rails
prometheus_exporter (~> 2.2)
propshaft
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ActivityPub::QuoteAuthorizationsController < ActivityPub::BaseController
before_action :set_quote_authorization

def show
expires_in 30.seconds, public: true if @quote.status.distributable? && public_fetch_mode?
expires_in 30.seconds, public: true if @quote.quoted_status.distributable? && public_fetch_mode?
render json: @quote, serializer: ActivityPub::QuoteAuthorizationSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end

Expand All @@ -23,7 +23,7 @@ def set_quote_authorization
@quote = Quote.accepted.where(quoted_account: @account).find(params[:id])
return not_found unless @quote.status.present? && @quote.quoted_status.present?

authorize @quote.status, :show?
authorize @quote.quoted_status, :show?
rescue Mastodon::NotPermittedError
not_found
end
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/api/v1/statuses_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,11 @@ def destroy
@status = Status.where(account: current_account).find(params[:id])
authorize @status, :destroy?

json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true

@status.discard_with_reblogs
StatusPin.find_by(status: @status)&.destroy
@status.account.statuses_count = @status.account.statuses_count - 1
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true

RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })

Expand Down
7 changes: 5 additions & 2 deletions app/controllers/follower_accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class FollowerAccountsController < ApplicationController
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }

before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :protect_hidden_collections, if: -> { request.format.json? }

skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :limited_federation_mode?
Expand All @@ -18,8 +19,6 @@ def index
end

format.json do
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?

expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)

render json: collection_presenter,
Expand All @@ -41,6 +40,10 @@ def follows
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
end

def protect_hidden_collections
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
end

def page_requested?
params[:page].present?
end
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/following_accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class FollowingAccountsController < ApplicationController
vary_by -> { public_fetch_mode? ? 'Accept, Accept-Language, Cookie' : 'Accept, Accept-Language, Cookie, Signature' }

before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
before_action :protect_hidden_collections, if: -> { request.format.json? }

skip_around_action :set_locale, if: -> { request.format == :json }
skip_before_action :require_functional!, unless: :limited_federation_mode?
Expand All @@ -18,11 +19,6 @@ def index
end

format.json do
if page_requested? && @account.hide_collections?
forbidden
next
end

expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)

render json: collection_presenter,
Expand All @@ -44,6 +40,10 @@ def follows
@follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
end

def protect_hidden_collections
raise Mastodon::NotPermittedError if page_requested? && @account.hide_collections?
end

def page_requested?
params[:page].present?
end
Expand Down
3 changes: 2 additions & 1 deletion app/javascript/mastodon/actions/compose_typed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ export const pasteLinkCompose = createDataLoadingThunk(
composeState.get('is_submitting') ||
composeState.get('poll') ||
composeState.get('is_uploading') ||
composeState.get('id')
composeState.get('id') ||
composeState.get('privacy') === 'direct'
)
return;

Expand Down
126 changes: 126 additions & 0 deletions app/javascript/mastodon/components/carousel/carousel.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type { FC } from 'react';

import type { Meta, StoryObj } from '@storybook/react-vite';
import { fn, userEvent, expect } from 'storybook/test';

import type { CarouselProps } from './index';
import { Carousel } from './index';

interface TestSlideProps {
id: number;
text: string;
color: string;
}

const TestSlide: FC<TestSlideProps & { active: boolean }> = ({
active,
text,
color,
}) => (
<div
className='test-slide'
style={{
backgroundColor: active ? color : undefined,
}}
>
{text}
</div>
);

const slides: TestSlideProps[] = [
{
id: 1,
text: 'first',
color: 'red',
},
{
id: 2,
text: 'second',
color: 'pink',
},
{
id: 3,
text: 'third',
color: 'orange',
},
];

type StoryProps = Pick<
CarouselProps<TestSlideProps>,
'items' | 'renderItem' | 'emptyFallback' | 'onChangeSlide'
>;

const meta = {
title: 'Components/Carousel',
args: {
items: slides,
renderItem(item, active) {
return <TestSlide {...item} active={active} key={item.id} />;
},
onChangeSlide: fn(),
emptyFallback: 'No slides available',
},
render(args) {
return (
<>
<Carousel {...args} />
<style>
{`.test-slide {
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
font-weight: bold;
min-height: 100px;
transition: background-color 0.3s;
background-color: black;
}`}
</style>
</>
);
},
argTypes: {
emptyFallback: {
type: 'string',
},
},
tags: ['test'],
} satisfies Meta<StoryProps>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
async play({ args, canvas }) {
const nextButton = await canvas.findByRole('button', { name: /next/i });
const slides = await canvas.findAllByRole('group');
await expect(slides).toHaveLength(slides.length);

await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(1, slides[1]);

await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(2, slides[2]);

// Wrap around
await userEvent.click(nextButton);
await expect(args.onChangeSlide).toHaveBeenCalledWith(0, slides[0]);
},
};

export const DifferentHeights: Story = {
args: {
items: slides.map((props, index) => ({
...props,
styles: { height: 100 + index * 100 },
})),
},
};

export const NoSlides: Story = {
args: {
items: [],
},
};
Loading