diff --git a/app/javascript/flavours/glitch/features/account_timeline/common.ts b/app/javascript/flavours/glitch/features/account_timeline/common.ts
index 33d1ee210d..866b2549e6 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/common.ts
+++ b/app/javascript/flavours/glitch/features/account_timeline/common.ts
@@ -1,5 +1,5 @@
-import { isClientFeatureEnabled } from '@/flavours/glitch/utils/environment';
+import { isServerFeatureEnabled } from '@/flavours/glitch/utils/environment';
export function isRedesignEnabled() {
- return isClientFeatureEnabled('profile_redesign');
+ return isServerFeatureEnabled('profile_redesign');
}
diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx
index 47c7b15f36..d692359300 100644
--- a/app/javascript/flavours/glitch/features/ui/index.jsx
+++ b/app/javascript/flavours/glitch/features/ui/index.jsx
@@ -25,6 +25,7 @@ import { layoutFromWindow } from 'flavours/glitch/is_mobile';
import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { checkAnnualReport } from '@/flavours/glitch/reducers/slices/annual_report';
+import { isServerFeatureEnabled } from '@/flavours/glitch/utils/environment';
import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
import { clearHeight } from '../../actions/height_cache';
@@ -92,7 +93,6 @@ import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
// Without this it ends up in ~8 very commonly used bundles.
import '../../components/status';
import { areCollectionsEnabled } from '../collections/utils';
-import { isClientFeatureEnabled } from '@/flavours/glitch/utils/environment';
const messages = defineMessages({
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
@@ -170,7 +170,7 @@ class SwitchingColumnsArea extends PureComponent {
redirect = ;
}
- const profileRedesignEnabled = isClientFeatureEnabled('profile_redesign');
+ const profileRedesignEnabled = isServerFeatureEnabled('profile_redesign');
const profileRedesignRoutes = [];
if (profileRedesignEnabled) {
profileRedesignRoutes.push(
diff --git a/app/javascript/flavours/glitch/features/ui/util/async-components.js b/app/javascript/flavours/glitch/features/ui/util/async-components.js
index bc76a9c019..db5cdf9f2a 100644
--- a/app/javascript/flavours/glitch/features/ui/util/async-components.js
+++ b/app/javascript/flavours/glitch/features/ui/util/async-components.js
@@ -1,4 +1,4 @@
-import { isClientFeatureEnabled } from '@/flavours/glitch/utils/environment';
+import { isServerFeatureEnabled } from '@/flavours/glitch/utils/environment';
export function EmojiPicker () {
return import('../../emoji/emoji_picker');
@@ -73,7 +73,7 @@ export function PinnedStatuses () {
}
export function AccountTimeline () {
- if (isClientFeatureEnabled('profile_redesign')) {
+ if (isServerFeatureEnabled('profile_redesign')) {
return import('../../account_timeline/v2');
}
return import('../../account_timeline');
diff --git a/app/javascript/flavours/glitch/utils/environment.ts b/app/javascript/flavours/glitch/utils/environment.ts
index c7fecee022..5f736fa80c 100644
--- a/app/javascript/flavours/glitch/utils/environment.ts
+++ b/app/javascript/flavours/glitch/utils/environment.ts
@@ -12,13 +12,13 @@ export function isProduction() {
else return import.meta.env.PROD;
}
-export type ServerFeatures = 'fasp' | 'collections';
+export type ServerFeatures = 'fasp' | 'collections' | 'profile_redesign';
export function isServerFeatureEnabled(feature: ServerFeatures) {
return initialState?.features.includes(feature) ?? false;
}
-type ClientFeatures = 'profile_redesign' | 'collections';
+type ClientFeatures = 'collections';
export function isClientFeatureEnabled(feature: ClientFeatures) {
try {
diff --git a/app/javascript/mastodon/features/account_timeline/common.ts b/app/javascript/mastodon/features/account_timeline/common.ts
index ee76c25bb7..1bb62a4220 100644
--- a/app/javascript/mastodon/features/account_timeline/common.ts
+++ b/app/javascript/mastodon/features/account_timeline/common.ts
@@ -1,5 +1,5 @@
-import { isClientFeatureEnabled } from '@/mastodon/utils/environment';
+import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
export function isRedesignEnabled() {
- return isClientFeatureEnabled('profile_redesign');
+ return isServerFeatureEnabled('profile_redesign');
}
diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx
index 23025cf16c..c2fcd2d02f 100644
--- a/app/javascript/mastodon/features/ui/index.jsx
+++ b/app/javascript/mastodon/features/ui/index.jsx
@@ -22,6 +22,7 @@ import { identityContextPropShape, withIdentity } from 'mastodon/identity_contex
import { layoutFromWindow } from 'mastodon/is_mobile';
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { checkAnnualReport } from '@/mastodon/reducers/slices/annual_report';
+import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
import { clearHeight } from '../../actions/height_cache';
@@ -89,7 +90,6 @@ import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
// Without this it ends up in ~8 very commonly used bundles.
import '../../components/status';
import { areCollectionsEnabled } from '../collections/utils';
-import { isClientFeatureEnabled } from '@/mastodon/utils/environment';
const messages = defineMessages({
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
@@ -162,7 +162,7 @@ class SwitchingColumnsArea extends PureComponent {
redirect = ;
}
- const profileRedesignEnabled = isClientFeatureEnabled('profile_redesign');
+ const profileRedesignEnabled = isServerFeatureEnabled('profile_redesign');
const profileRedesignRoutes = [];
if (profileRedesignEnabled) {
profileRedesignRoutes.push(
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js
index 27c634d07c..55fcc29fa4 100644
--- a/app/javascript/mastodon/features/ui/util/async-components.js
+++ b/app/javascript/mastodon/features/ui/util/async-components.js
@@ -1,4 +1,4 @@
-import { isClientFeatureEnabled } from '@/mastodon/utils/environment';
+import { isServerFeatureEnabled } from '@/mastodon/utils/environment';
export function EmojiPicker () {
return import('../../emoji/emoji_picker');
@@ -73,7 +73,7 @@ export function PinnedStatuses () {
}
export function AccountTimeline () {
- if (isClientFeatureEnabled('profile_redesign')) {
+ if (isServerFeatureEnabled('profile_redesign')) {
return import('../../account_timeline/v2');
}
return import('../../account_timeline');
diff --git a/app/javascript/mastodon/utils/environment.ts b/app/javascript/mastodon/utils/environment.ts
index c7fecee022..5f736fa80c 100644
--- a/app/javascript/mastodon/utils/environment.ts
+++ b/app/javascript/mastodon/utils/environment.ts
@@ -12,13 +12,13 @@ export function isProduction() {
else return import.meta.env.PROD;
}
-export type ServerFeatures = 'fasp' | 'collections';
+export type ServerFeatures = 'fasp' | 'collections' | 'profile_redesign';
export function isServerFeatureEnabled(feature: ServerFeatures) {
return initialState?.features.includes(feature) ?? false;
}
-type ClientFeatures = 'profile_redesign' | 'collections';
+type ClientFeatures = 'collections';
export function isClientFeatureEnabled(feature: ClientFeatures) {
try {
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 404e1b4f92..0b6158b87b 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -2,6 +2,7 @@
class PostStatusService < BaseService
include Redisable
+ include Lockable
include LanguagesHelper
class UnexpectedMentionsError < StandardError
@@ -39,19 +40,17 @@ class PostStatusService < BaseService
@in_reply_to = @options[:thread]
@quoted_status = @options[:quoted_status]
- return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
+ with_idempotency do
+ validate_media!
+ preprocess_attributes!
- validate_media!
- preprocess_attributes!
-
- if scheduled?
- schedule_status!
- else
- process_status!
+ if scheduled?
+ schedule_status!
+ else
+ process_status!
+ end
end
- redis.setex(idempotency_key, 3_600, @status.id) if idempotency_given?
-
unless scheduled?
postprocess_status!
bump_potential_friendship!
@@ -224,6 +223,18 @@ class PostStatusService < BaseService
@idempotency_duplicate = redis.get(idempotency_key)
end
+ def with_idempotency
+ return yield unless idempotency_given?
+
+ with_redis_lock("idempotency:lock:status:#{@account.id}:#{@options[:idempotency]}") do
+ return idempotency_duplicate if idempotency_duplicate?
+
+ yield
+
+ redis.setex(idempotency_key, 3_600, @status.id)
+ end
+ end
+
def scheduled_in_the_past?
@scheduled_at.present? && @scheduled_at <= Time.now.utc
end
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 7fff363ff1..87334b14e7 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -224,6 +224,7 @@ en:
email: E-mail address
expires_in: Expire after
fields: Extra fields
+ filter_action: Filter action
header: Header picture
honeypot: "%{label} (do not fill in)"
inbox_url: URL of the relay inbox