Merge commit 'f16f8b51b863d3fcadd17753004adbf8962ead01' into glitch-soc/merge-upstream

Conflicts:
- `yarn.lock`:
  Upstream updated a dependency adjacent to one that is only in glitch-soc.
  Updated as upstream did.
This commit is contained in:
Claire committed 2025-08-19 18:46:12 +02:00
commit 71cee3e472
39 files changed
+278 -152

No files matched your search

@@ -53,6 +53,7 @@ const AutosuggestTextarea = forwardRef(({
onFocus,
autoFocus = true,
lang,
className,
}, textareaRef) => {
const [suggestionsHidden, setSuggestionsHidden] = useState(true);
@@ -192,7 +193,7 @@ const AutosuggestTextarea = forwardRef(({
};
return (
<div className='autosuggest-textarea'>
<div className={classNames('autosuggest-textarea', className)}>
<Textarea
ref={textareaRef}
className='autosuggest-textarea__textarea'
@@ -10,6 +10,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import CancelFillIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
import { Hotkeys } from 'mastodon/components/hotkeys';
import { ContentWarning } from 'mastodon/components/content_warning';
import { FilterWarning } from 'mastodon/components/filter_warning';
@@ -34,6 +35,8 @@ import StatusActionBar from './status_action_bar';
import StatusContent from './status_content';
import { StatusThreadLabel } from './status_thread_label';
import { VisibilityIcon } from './visibility_icon';
import { IconButton } from './icon_button';
const domParser = new DOMParser();
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
@@ -75,6 +78,7 @@ const messages = defineMessages({
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Specific people' },
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
quote_cancel: { id: 'status.quote.cancel', defaultMessage: 'Cancel quote' },
});
class Status extends ImmutablePureComponent {
@@ -126,6 +130,7 @@ class Status extends ImmutablePureComponent {
inUse: PropTypes.bool,
available: PropTypes.bool,
}),
contextType: PropTypes.string,
...WithOptionalRouterPropTypes,
};
@@ -359,6 +364,10 @@ class Status extends ImmutablePureComponent {
this.setState(state => ({ ...state, showDespiteFilter: !state.showDespiteFilter }));
};
handleQuoteCancel = () => {
this.props.onQuoteCancel?.();
}
_properStatus () {
const { status } = this.props;
@@ -573,6 +582,16 @@ class Status extends ImmutablePureComponent {
<DisplayName account={status.get('account')} />
</Link>
{this.props.contextType === 'compose' && isQuotedPost && (
<IconButton
onClick={this.handleQuoteCancel}
className='status__quote-cancel'
title={intl.formatMessage(messages.quote_cancel)}
icon="cancel-fill"
iconComponent={CancelFillIcon}
/>
)}
</div>
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
@@ -44,6 +44,7 @@ import {
import Status from '../components/status';
import { deleteModal } from '../initial_state';
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
import { quoteComposeCancel } from '../actions/compose_typed';
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
@@ -111,6 +112,12 @@ const mapDispatchToProps = (dispatch, { contextType }) => ({
}
},
onQuoteCancel() {
if (contextType === 'compose') {
dispatch(quoteComposeCancel());
}
},
onRevokeQuote (status) {
dispatch(openModal({ modalType: 'CONFIRM_REVOKE_QUOTE', modalProps: { statusId: status.get('id'), quotedStatusId: status.getIn(['quote', 'quoted_status']) }}));
},
@@ -31,6 +31,7 @@ import { PollForm } from "./poll_form";
import { ReplyIndicator } from './reply_indicator';
import { UploadForm } from './upload_form';
import { Warning } from './warning';
import { ComposeQuotedStatus } from './quoted_post';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
@@ -258,6 +259,11 @@ class ComposeForm extends ImmutablePureComponent {
<div className={classNames('compose-form__highlightable', { active: highlighted })} ref={this.setRef}>
<EditIndicator />
<div className='compose-form__dropdowns'>
<PrivacyDropdownContainer disabled={this.props.isEditing} />
<LanguageDropdown />
</div>
{this.props.spoiler && (
<div className='spoiler-input'>
<div className='spoiler-input__border' />
@@ -284,11 +290,6 @@ class ComposeForm extends ImmutablePureComponent {
</div>
)}
<div className='compose-form__dropdowns'>
<PrivacyDropdownContainer disabled={this.props.isEditing} />
<LanguageDropdown />
</div>
<AutosuggestTextarea
ref={this.textareaRef}
placeholder={intl.formatMessage(messages.placeholder)}
@@ -304,10 +305,12 @@ class ComposeForm extends ImmutablePureComponent {
onPaste={onPaste}
autoFocus={autoFocus}
lang={this.props.lang}
className='compose-form__input'
/>
<UploadForm />
<PollForm />
<ComposeQuotedStatus />
<div className='compose-form__footer'>
<div className='compose-form__actions'>
@@ -0,0 +1,27 @@
import { useMemo } from 'react';
import type { FC } from 'react';
import { Map } from 'immutable';
import { QuotedStatus } from '@/mastodon/components/status_quoted';
import { useAppSelector } from '@/mastodon/store';
export const ComposeQuotedStatus: FC = () => {
const quotedStatusId = useAppSelector(
(state) => state.compose.get('quoted_status_id') as string | null,
);
const quote = useMemo(
() =>
quotedStatusId
? Map<'state' | 'quoted_status', string>([
['state', 'accepted'],
['quoted_status', quotedStatusId],
])
: null,
[quotedStatusId],
);
if (!quote) {
return null;
}
return <QuotedStatus quote={quote} contextType='compose' />;
};
@@ -3,10 +3,16 @@ import { connect } from 'react-redux';
import { addPoll, removePoll } from '../../../actions/compose';
import PollButton from '../components/poll_button';
const mapStateToProps = state => ({
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
active: state.getIn(['compose', 'poll']) !== null,
});
const mapStateToProps = state => {
const readyAttachmentsSize = state.compose.get('media_attachments').size ?? 0;
const hasAttachments = readyAttachmentsSize > 0 || !!state.compose.get('is_uploading');
const hasQuote = !!state.compose.get('quoted_status_id');
return ({
disabled: hasAttachments || hasQuote,
active: state.getIn(['compose', 'poll']) !== null,
});
};
const mapDispatchToProps = dispatch => ({
@@ -11,9 +11,10 @@ const mapStateToProps = state => {
const attachmentsSize = readyAttachmentsSize + pendingAttachmentsSize;
const isOverLimit = attachmentsSize > state.getIn(['server', 'server', 'configuration', 'statuses', 'max_media_attachments'])-1;
const hasVideoOrAudio = state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')));
const hasQuote = !!state.compose.get('quoted_status_id');
return {
disabled: isPoll || isUploading || isOverLimit || hasVideoOrAudio,
disabled: isPoll || isUploading || isOverLimit || hasVideoOrAudio || hasQuote,
resetFileKey: state.getIn(['compose', 'resetFileKey']),
};
};
+2
View File
@@ -864,6 +864,7 @@
"status.mute": "@{name} - səssizə al",
"status.mute_conversation": "Danışığın səsini kəs",
"status.open": "Bu göndərişi genişləndir",
"status.pin": "Profildə sanc",
"status.quote_error.filtered": "Bəzi filtrlərinizə görə gizlidir",
"status.quote_error.not_available": "Göndəriş əlçatmazdır",
"status.quote_error.pending_approval": "Göndəriş gözləmədədir",
@@ -893,6 +894,7 @@
"status.translated_from_with": "{provider} ilə {lang} dilindən tərcümə edilib",
"status.uncached_media_warning": "Önizləmə mövcud deyil",
"status.unmute_conversation": "Danışığın səsini aç",
"status.unpin": "Profil sancağı götür",
"subscribed_languages.lead": "Dəyişiklikdən sonra əsas ekran və siyahı zaman xəttinizdə yalnız seçdiyiniz dillərdəki göndərişlər görünəcək. Bütün dillərdə göndəriş almaq üçün heç birini seçməyin.",
"subscribed_languages.save": "Dəyişiklikləri saxla",
"subscribed_languages.target": "{target} üçün abunə olunmuş dilləri dəyişdir",
+1
View File
@@ -879,6 +879,7 @@
"status.mute_conversation": "Silencia la conversa",
"status.open": "Amplia el tut",
"status.pin": "Fixa en el perfil",
"status.quote.cancel": "Canceŀlar la citació",
"status.quote_error.filtered": "No es mostra a causa d'un dels vostres filtres",
"status.quote_error.not_available": "Publicació no disponible",
"status.quote_error.pending_approval": "Publicació pendent",
+2 -1
View File
@@ -63,7 +63,7 @@
"account.mute_short": "Stummschalten",
"account.muted": "Stummgeschaltet",
"account.muting": "Stummgeschaltet",
"account.mutual": "Ihr folgt euch",
"account.mutual": "Ihr folgt einander",
"account.no_bio": "Keine Beschreibung verfügbar.",
"account.open_original_page": "Ursprüngliche Seite öffnen",
"account.posts": "Beiträge",
@@ -880,6 +880,7 @@
"status.mute_conversation": "Unterhaltung stummschalten",
"status.open": "Beitrag öffnen",
"status.pin": "Im Profil anheften",
"status.quote.cancel": "Zitat abbrechen",
"status.quote_error.filtered": "Ausgeblendet wegen eines deiner Filter",
"status.quote_error.not_available": "Beitrag nicht verfügbar",
"status.quote_error.pending_approval": "Beitragsveröffentlichung ausstehend",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this post",
"status.pin": "Pin on profile",
"status.quote.cancel": "Cancel quote",
"status.quote_error.filtered": "Hidden due to one of your filters",
"status.quote_error.not_available": "Post unavailable",
"status.quote_error.pending_approval": "Post pending",
@@ -880,6 +880,7 @@
"status.mute_conversation": "Silenciar conversación",
"status.open": "Expandir este mensaje",
"status.pin": "Fijar en el perfil",
"status.quote.cancel": "Cancelar cita",
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
"status.quote_error.not_available": "Mensaje no disponible",
"status.quote_error.pending_approval": "Mensaje pendiente",
@@ -880,6 +880,7 @@
"status.mute_conversation": "Silenciar conversación",
"status.open": "Expandir estado",
"status.pin": "Fijar",
"status.quote.cancel": "Cancelar cita",
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
"status.quote_error.not_available": "Publicación no disponible",
"status.quote_error.pending_approval": "Publicación pendiente",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Silenciar conversación",
"status.open": "Expandir publicación",
"status.pin": "Fijar",
"status.quote.cancel": "Cancelar cita",
"status.quote_error.filtered": "Oculto debido a uno de tus filtros",
"status.quote_error.not_available": "Publicación no disponible",
"status.quote_error.pending_approval": "Publicación pendiente",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Mykistä keskustelu",
"status.open": "Laajenna julkaisu",
"status.pin": "Kiinnitä profiiliin",
"status.quote.cancel": "Peruuta lainaus",
"status.quote_error.filtered": "Piilotettu jonkin asettamasi suodattimen takia",
"status.quote_error.not_available": "Julkaisu ei saatavilla",
"status.quote_error.pending_approval": "Julkaisu odottaa",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Doyv samrøðu",
"status.open": "Víðka henda postin",
"status.pin": "Ger fastan í vangan",
"status.quote.cancel": "Ógilda sitat",
"status.quote_error.filtered": "Eitt av tínum filtrum fjalir hetta",
"status.quote_error.not_available": "Postur ikki tøkur",
"status.quote_error.pending_approval": "Postur bíðar",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Silenciar conversa",
"status.open": "Estender esta publicación",
"status.pin": "Fixar no perfil",
"status.quote.cancel": "Cancelar a cita",
"status.quote_error.filtered": "Oculto debido a un dos teus filtros",
"status.quote_error.not_available": "Publicación non dispoñible",
"status.quote_error.pending_approval": "Publicación pendente",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "השתקת שיחה",
"status.open": "הרחבת הודעה זו",
"status.pin": "הצמדה לפרופיל שלי",
"status.quote.cancel": "ביטול הודעת ציטוט",
"status.quote_error.filtered": "מוסתר בהתאם לסננים שלך",
"status.quote_error.not_available": "ההודעה לא זמינה",
"status.quote_error.pending_approval": "ההודעה בהמתנה לאישור",
+6
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Þagga niður í samtali",
"status.open": "Opna þessa færslu",
"status.pin": "Festa á notandasnið",
"status.quote.cancel": "Hætta við tilvitnun",
"status.quote_error.filtered": "Falið vegna einnar síu sem er virk",
"status.quote_error.not_available": "Færsla ekki tiltæk",
"status.quote_error.pending_approval": "Færsla í bið",
@@ -964,6 +965,11 @@
"video.volume_up": "Hækka hljóðstyrk",
"visibility_modal.button_title": "Stilla sýnileika",
"visibility_modal.header": "Sýnileiki og gagnvirkni",
"visibility_modal.helper.direct_quoting": "Ekki er hægt að vitna í einkaspjall.",
"visibility_modal.helper.privacy_editing": "Ekki er hægt að breyta sýnileika birtra færslna.",
"visibility_modal.helper.private_quoting": "Ekki er hægt að vitna í færslur sem eingöngu eru til fylgjenda.",
"visibility_modal.helper.unlisted_quoting": "Þegar fólk vitnar í þig verða færslurnar þeirr einnig faldar á vinsældatímalínum.",
"visibility_modal.instructions": "Stýrðu hverjir geta átt við þessa færslu. Víðværar stillingar finnast undir <link>Kjörstillingar > Annað</link>.",
"visibility_modal.privacy_label": "Persónuvernd",
"visibility_modal.quote_followers": "Einungis fylgjendur",
"visibility_modal.quote_label": "Breyttu því hver getur tilvitnað",
+5 -1
View File
@@ -603,6 +603,7 @@
"notification.label.mention": "Упоминание",
"notification.label.private_mention": "Личное упоминание",
"notification.label.private_reply": "Приватный ответ",
"notification.label.quote": "{name} процитировал(а) ваш пост",
"notification.label.reply": "Ответ",
"notification.mention": "Упоминание",
"notification.mentioned_you": "{name} упомянул(а) вас",
@@ -660,6 +661,7 @@
"notifications.column_settings.mention": "Вас упомянули в посте:",
"notifications.column_settings.poll": "Опрос, в котором вы приняли участие, завершился:",
"notifications.column_settings.push": "Push-уведомления",
"notifications.column_settings.quote": "Ваш пост процитировали:",
"notifications.column_settings.reblog": "Ваш пост продвинули:",
"notifications.column_settings.show": "Отображать в списке",
"notifications.column_settings.sound": "Проигрывать звук",
@@ -851,7 +853,7 @@
"status.cancel_reblog_private": "Отменить продвижение",
"status.cannot_reblog": "Этот пост не может быть продвинут",
"status.context.load_new_replies": "Доступны новые ответы",
"status.context.loading": "Проверяем, есть ли еще ответы",
"status.context.loading": "Проверяем, есть ли ещё ответы",
"status.continued_thread": "Продолжение предыдущего поста",
"status.copy": "Скопировать ссылку на пост",
"status.delete": "Удалить",
@@ -879,6 +881,8 @@
"status.pin": "Закрепить в профиле",
"status.quote_error.filtered": "Скрыто одним из ваших фильтров",
"status.quote_error.not_available": "Пост недоступен",
"status.quote_error.pending_approval": "Пост ожидает подтверждения",
"status.quote_error.pending_approval_popout.title": "Цитирование ещё не подтверждено? Не беспокойтесь",
"status.read_more": "Читать далее",
"status.reblog": "Продвинуть",
"status.reblog_private": "Продвинуть для своей аудитории",
+1
View File
@@ -880,6 +880,7 @@
"status.mute_conversation": "Tắt thông báo",
"status.open": "Mở tút",
"status.pin": "Ghim lên hồ sơ",
"status.quote.cancel": "Bỏ trích dẫn",
"status.quote_error.filtered": "Bị ẩn vì một bộ lọc của bạn",
"status.quote_error.not_available": "Tút không khả dụng",
"status.quote_error.pending_approval": "Tút đang chờ duyệt",
@@ -240,6 +240,8 @@
"confirmations.remove_from_followers.confirm": "移除关注者",
"confirmations.remove_from_followers.message": "{name} 将停止关注您。您确定要继续吗?",
"confirmations.remove_from_followers.title": "移除关注者?",
"confirmations.revoke_quote.confirm": "移除嘟文",
"confirmations.revoke_quote.title": "移除嘟文?",
"confirmations.unfollow.confirm": "取消关注",
"confirmations.unfollow.message": "你确定要取消关注 {name} 吗?",
"confirmations.unfollow.title": "确定要取消关注用户?",
@@ -491,6 +493,7 @@
"keyboard_shortcuts.translate": "翻译嘟文",
"keyboard_shortcuts.unfocus": "取消输入/搜索",
"keyboard_shortcuts.up": "在列表中让光标上移",
"learn_more_link.got_it": "明白了",
"lightbox.close": "关闭",
"lightbox.next": "下一个",
"lightbox.previous": "上一个",
@@ -861,6 +864,7 @@
"status.mute_conversation": "关闭此对话的通知",
"status.open": "展开嘟文",
"status.pin": "在个人资料页面置顶",
"status.quote.cancel": "取消引用",
"status.quote_error.filtered": "已根据你的筛选器过滤",
"status.read_more": "查看更多",
"status.reblog": "转嘟",
@@ -880,6 +880,7 @@
"status.mute_conversation": "靜音對話",
"status.open": "展開此嘟文",
"status.pin": "釘選至個人檔案頁面",
"status.quote.cancel": "取消引用嘟文",
"status.quote_error.filtered": "由於您的過濾器,該嘟文被隱藏",
"status.quote_error.not_available": "無法取得該嘟文",
"status.quote_error.pending_approval": "嘟文正在發送中",
+10 -2
View File
@@ -331,8 +331,16 @@ export const composeReducer = (state = initialState, action) => {
return state.set('is_changing_upload', false);
} else if (quoteComposeByStatus.match(action)) {
const status = action.payload;
if (status.getIn(['quote_approval', 'current_user']) === 'automatic') {
return state.set('quoted_status_id', status.get('id'));
if (
status.getIn(['quote_approval', 'current_user']) === 'automatic' &&
state.get('media_attachments').size === 0 &&
!state.get('is_uploading') &&
!state.get('poll')
) {
return state
.set('quoted_status_id', status.get('id'))
.set('spoiler', status.get('sensitive'))
.set('spoiler_text', status.get('spoiler_text'));
}
} else if (quoteComposeCancel.match(action)) {
return state.set('quoted_status_id', null);
+1 -1
View File
@@ -15,7 +15,7 @@ export const getFilters = createSelector(
(_, { contextType }: { contextType: string }) => contextType,
],
(filters, contextType) => {
if (!contextType) {
if (!contextType || contextType === 'compose') {
return null;
}
@@ -129,7 +129,7 @@ export function createAppThunk<Arg = void, Returned = void, ExtraArg = unknown>(
},
}));
return Object.assign({}, action, actionCreator);
return Object.assign(actionCreator, action);
}
const createBaseAsyncThunk = rtkCreateAsyncThunk.withTypes<AppThunkConfig>();