Merge commit '2b93d19d2ca18366b015e3dcde412e67625fe8f5' into glitch-soc/merge-upstream

This commit is contained in:
Claire 2026-04-14 18:19:27 +02:00
commit 817291c692
28 changed files with 475 additions and 33 deletions

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
class Api::V1Alpha::InCollectionsController < Api::BaseController
include Authorization
DEFAULT_COLLECTIONS_LIMIT = 40
before_action :check_feature_enabled
before_action -> { authorize_if_got_token! :read, :'read:collections' }, only: [:index]
before_action :require_user!
before_action :set_account, only: [:index]
before_action :set_collections, only: [:index]
after_action :insert_pagination_headers, only: [:index]
after_action :verify_authorized
def index
cache_if_unauthenticated!
authorize @account, :index_featured_in_collections?
render json: @collections, each_serializer: REST::CollectionSerializer, adapter: :json
end
private
def set_account
@account = Account.find(params[:account_id])
end
def set_collections
@collections = @account.featured_in_collections
.with_tag
.offset(offset_param)
.limit(limit_param(DEFAULT_COLLECTIONS_LIMIT))
end
def check_feature_enabled
raise ActionController::RoutingError unless Mastodon::Feature.collections_enabled?
end
def next_path
return unless records_continue?
api_v1_alpha_account_in_collections_url(@account, pagination_params(offset: offset_param + limit_param(DEFAULT_COLLECTIONS_LIMIT)))
end
def prev_path
return if offset_param.zero?
api_v1_alpha_account_in_collections_url(@account, pagination_params(offset: offset_param - limit_param(DEFAULT_COLLECTIONS_LIMIT)))
end
def records_continue?
((offset_param * limit_param(DEFAULT_COLLECTIONS_LIMIT)) + @collections.size) < @account.featured_in_collections.size
end
def offset_param
params[:offset].to_i
end
end

View File

@ -64,15 +64,21 @@ export const ImageUploadModal: FC<
const [imageBlob, setImageBlob] = useState<Blob | null>(null);
const handleFile = useCallback((file: File) => {
const reader = new FileReader();
reader.addEventListener('load', () => {
const result = reader.result;
if (typeof result === 'string' && result.length > 0) {
setImageSrc(result);
setStep('crop');
}
});
reader.readAsDataURL(file);
try {
parseImageFile(file, (result, isAnimated) => {
if (isAnimated) {
// If the image is animated, skip cropping and go straight to alt text.
setImageBlob(file);
setStep('alt');
} else {
setImageSrc(result);
setStep('crop');
}
});
} catch (error) {
console.warn('Error with image parsing:', error);
setStep('select');
}
}, []);
const handleCrop = useCallback(
@ -104,19 +110,20 @@ export const ImageUploadModal: FC<
);
const handleCancel = useCallback(() => {
switch (step) {
case 'crop':
setImageSrc(null);
setStep('select');
break;
case 'alt':
setImageBlob(null);
if (step === 'crop') {
setImageSrc(null);
setStep('select');
} else if (step === 'alt') {
setImageBlob(null);
if (imageSrc) {
setStep('crop');
break;
default:
onClose();
} else {
setStep('select');
}
} else {
onClose();
}
}, [onClose, step]);
}, [imageSrc, onClose, step]);
return (
<DialogModal
@ -401,6 +408,54 @@ const StepAlt: FC<{
);
};
/**
* Parses an image file and determines if it's an animated GIF and returns a data URI for cropping.
* Based on https://gist.github.com/zakirt/faa4a58cec5a7505b10e3686a226f285.
*/
function parseImageFile(
file: File,
cb: (buffer: string, isAnimated: boolean) => void,
): void {
const reader = new FileReader();
reader.onload = () => {
const buffer = reader.result;
if (!(buffer instanceof ArrayBuffer)) {
throw new Error('Expected an ArrayBuffer');
}
// Convert the ArrayBuffer to a base64 data URI.
const bytes = new Uint8Array(buffer);
const base64 = btoa(String.fromCharCode(...bytes));
const dataUri = `data:${file.type};base64,${base64}`;
// If the file type is not a GIF, then it's not animated as we don't support animated WebP or PNG.
if (file.type !== 'image/gif') {
cb(dataUri, false);
}
const view = new DataView(buffer, 10); // Start from the last 4 bytes of the Logical Screen Descriptor.
let offset = 3;
// Check the first bit for the global color table flag.
const globalColorTable = view.getInt8(0);
if (globalColorTable & 0x08) {
// Grab last three bits to calculate the global color table size, and skip it.
offset += 3 * Math.pow(2, (globalColorTable & 0x7) + 1);
}
// Check Graphics Control Extension and Graphics Control Label to access animated data.
let delayTime = 0;
if (view.getUint8(offset) & 0x21 && view.getUint8(offset + 1) & 0xf9) {
// Skip to the delay time, which is stored in the next two bytes.
delayTime = view.getUint16(offset + 4);
}
// If there is a delay time, the GIF is animated.
cb(dataUri, delayTime > 0);
};
reader.readAsArrayBuffer(file);
}
async function calculateCroppedImage(
imageSrc: string,
crop: Area,
@ -427,10 +482,7 @@ async function calculateCroppedImage(
crop.height,
);
return canvas.convertToBlob({
quality: 0.7,
type: 'image/jpeg',
});
return canvas.convertToBlob();
}
function dataUriToImage(dataUri: string) {

View File

@ -189,7 +189,7 @@ const AccountNameHelp: FC<{
</ol>
<FormattedMessage
id='account.name.help.footer'
defaultMessage='Just like you can send emails to people using different email clients, you can interact with people on other Mastodon servers and with anyone on other social apps powered by the same set of rules as Mastodon uses (the ActivityPub protocol).'
defaultMessage='Just like you can send emails to people using different email providers, you can interact with people on other Mastodon servers, and with anyone on other Mastodon-compatible social apps.'
tagName='p'
/>

View File

@ -67,6 +67,10 @@
flex-wrap: nowrap;
overflow: hidden;
position: relative;
> button {
flex-shrink: 0;
}
}
.tagsListShowAll {

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Вас уключылі ў гэтую калекцыю",
"collections.edit_details": "Рэдагаваць падрабязнасці",
"collections.error_loading_collections": "Адбылася памылка падчас загрузкі Вашых калекцый.",
"collections.hidden_accounts_description": "Вы заблакіравалі або ігнаруеце {count, plural, one {гэтага карыстальніка} other {гэтых карыстальнікаў}}",
"collections.hidden_accounts_link": "{count, plural, one {# схаваны ўліковы запіс} few {# схаваныя ўліковыя запісы} other {# схаваных уліковых запісаў}}",
"collections.hints.accounts_counter": "{count} / {max} уліковых запісаў",
"collections.last_updated_at": "Апошняе абнаўленне: {date}",
"collections.manage_accounts": "Кіраванне ўліковымі запісамі",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Du er med i denne samling",
"collections.edit_details": "Rediger detaljer",
"collections.error_loading_collections": "Der opstod en fejl under indlæsning af dine samlinger.",
"collections.hidden_accounts_description": "Du har blokeret eller skjult {count, plural, one {denne bruger} other {disse brugere}}",
"collections.hidden_accounts_link": "{count, plural, one {# skjult konto} other {# skjulte konti}}",
"collections.hints.accounts_counter": "{count} / {max} konti",
"collections.last_updated_at": "Senest opdateret: {date}",
"collections.manage_accounts": "Administrer konti",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Du bist ein Teil dieser Sammlung",
"collections.edit_details": "Details bearbeiten",
"collections.error_loading_collections": "Beim Laden deiner Sammlungen ist ein Fehler aufgetreten.",
"collections.hidden_accounts_description": "Du hast {count, plural, one {dieses Profil} other {diese Profile}} blockiert oder stummgeschaltet",
"collections.hidden_accounts_link": "{count, plural, one {# Konto ausgeblendet} other {# Konten ausgeblendet}}",
"collections.hints.accounts_counter": "{count}/{max} Konten",
"collections.last_updated_at": "Aktualisiert: {date}",
"collections.manage_accounts": "Profile verwalten",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Είστε αναδεδειγμένοι σε αυτήν τη συλλογή",
"collections.edit_details": "Επεξεργασία λεπτομερειών",
"collections.error_loading_collections": "Παρουσιάστηκε σφάλμα κατά την προσπάθεια φόρτωσης των συλλογών σας.",
"collections.hidden_accounts_description": "Έχετε αποκλείσει ή κάνει σίγαση {count, plural, one {αυτόν τον χρήστη} other {αυτούς τους χρήστες}}",
"collections.hidden_accounts_link": "{count, plural, one {# κρυμμένος λογαριασμός} other {# κρυμμένοι λογαριασμοί}}",
"collections.hints.accounts_counter": "{count} / {max} λογαριασμοί",
"collections.last_updated_at": "Τελευταία ενημέρωση: {date}",
"collections.manage_accounts": "Διαχείριση λογαριασμών",

View File

@ -106,7 +106,7 @@
"account.name.copy": "Copy handle",
"account.name.help.domain": "{domain} is the server that hosts the users profile and posts.",
"account.name.help.domain_self": "{domain} is your server that hosts your profile and posts.",
"account.name.help.footer": "Just like you can send emails to people using different email clients, you can interact with people on other Mastodon servers and with anyone on other social apps powered by the same set of rules as Mastodon uses (the ActivityPub protocol).",
"account.name.help.footer": "Just like you can send emails to people using different email providers, you can interact with people on other Mastodon servers, and with anyone on other Mastodon-compatible social apps.",
"account.name.help.header": "A handle is like an email address",
"account.name.help.username": "{username} is this accounts username on their server. Someone on another server might have the same username.",
"account.name.help.username_self": "{username} is your username on this server. Someone on another server might have the same username.",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Te destacaron en esta colección",
"collections.edit_details": "Editar detalles",
"collections.error_loading_collections": "Hubo un error al intentar cargar tus colecciones.",
"collections.hidden_accounts_description": "Bloqueaste o silenciaste a {count, plural, one {este usuario} other {estos usuarios}}",
"collections.hidden_accounts_link": "{count, plural, one {# cuenta oculta} other {# cuentas ocultas}}",
"collections.hints.accounts_counter": "{count} / {max} cuentas",
"collections.last_updated_at": "Última actualización: {date}",
"collections.manage_accounts": "Administrar cuentas",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Apareces en esta colección",
"collections.edit_details": "Editar detalles",
"collections.error_loading_collections": "Se produjo un error al intentar cargar tus colecciones.",
"collections.hidden_accounts_description": "Has bloqueado o silenciado {count, plural, one {a este usuario} other {a estos usuarios}}",
"collections.hidden_accounts_link": "{count, plural, one {# cuenta oculta} other {# cuentas ocultas}}",
"collections.hints.accounts_counter": "{count} / {max} cuentas",
"collections.last_updated_at": "Última actualización: {date}",
"collections.manage_accounts": "Administrar cuentas",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Apareces en esta colección",
"collections.edit_details": "Editar detalles",
"collections.error_loading_collections": "Se ha producido un error al intentar cargar tus colecciones.",
"collections.hidden_accounts_description": "Has bloqueado o silenciado {count, plural,one {a este usuario}other {a estos usuarios}}",
"collections.hidden_accounts_link": "{count, plural, one {# cuenta oculta} other {# cuentas ocultas}}",
"collections.hints.accounts_counter": "{count} / {max} cuentas",
"collections.last_updated_at": "Última actualización: {date}",
"collections.manage_accounts": "Administrar cuentas",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Esiinnyt tässä kokoelmassa",
"collections.edit_details": "Muokkaa tietoja",
"collections.error_loading_collections": "Kokoelmien latauksessa tapahtui virhe.",
"collections.hidden_accounts_description": "Olet estänyt tai mykistänyt {count, plural, one {tämän käyttäjän} other {nämä käyttäjät}}",
"collections.hidden_accounts_link": "{count, plural, one {# piilotettu tili} other {# piilotettua tiliä}}",
"collections.hints.accounts_counter": "{count} / {max} tiliä",
"collections.last_updated_at": "Päivitetty viimeksi {date}",
"collections.manage_accounts": "Hallitse tilejä",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Þú kemur fyrir í þessu safni",
"collections.edit_details": "Breyta ítarupplýsingum",
"collections.error_loading_collections": "Villa kom upp þegar reynt var að hlaða inn söfnunum þínum.",
"collections.hidden_accounts_description": "Þú hefur lokað á eða þaggað niður í {count, plural, one {þessum notanda} other {þessum notendum}}",
"collections.hidden_accounts_link": "{count, plural, one {# falinn aðgangur} other {# faldir aðgangar}}",
"collections.hints.accounts_counter": "{count} / {max} aðgangar",
"collections.last_updated_at": "Síðast uppfært: {date}",
"collections.manage_accounts": "Sýsla með notandaaðganga",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Sei presente in questa collezione",
"collections.edit_details": "Modifica i dettagli",
"collections.error_loading_collections": "Si è verificato un errore durante il tentativo di caricare le tue collezioni.",
"collections.hidden_accounts_description": "Hai bloccato o silenziato {count, plural, one {questo utente} other {questi utenti}}",
"collections.hidden_accounts_link": "{count, plural, one {# account nascosto} other {# account nascosti}}",
"collections.hints.accounts_counter": "{count} / {max} account",
"collections.last_updated_at": "Ultimo aggiornamento: {date}",
"collections.manage_accounts": "Gestisci account",

View File

@ -194,27 +194,41 @@
"collections.accounts.empty_description": "フォローしている {count} アカウントまで追加できます",
"collections.accounts.empty_title": "このコレクションは空です",
"collections.by_account": "{account_handle} による",
"collections.collection_description": "詳細",
"collections.collection_language": "言語",
"collections.collection_name": "名前",
"collections.collection_topic": "トピック",
"collections.confirm_account_removal": "このコレクションからこのアカウントを削除してもよろしいですか?",
"collections.content_warning": "閲覧注意",
"collections.continue": "次に進む",
"collections.create.accounts_subtitle": "あなたがフォローしていてディスカバリー機能にオプトインしたアカウントのみを追加することができます。",
"collections.create.accounts_title": "このコレクションで誰に注目しますか?",
"collections.create.basic_details_title": "基本情報",
"collections.create.steps": "ステップ {step}/{total}",
"collections.create_a_collection_hint": "お気に入りのアカウントを他の人に勧めたり共有するコレクションを作成しましょう。",
"collections.create_collection": "コレクションを作成",
"collections.delete_collection": "コレクションを削除",
"collections.description_length_hint": "100 文字制限",
"collections.description_length_hint": "100 文字まで",
"collections.detail.accounts_heading": "アカウント",
"collections.detail.author_added_you_on_date": "{author} があなたを {date} に追加しました",
"collections.detail.loading": "コレクションを読み込み中…",
"collections.detail.share": "コレクションを共有",
"collections.detail.you_are_in_this_collection": "あなたはこのコレクションで紹介されています",
"collections.hints.accounts_counter": "{count} / {max} アカウント",
"collections.mark_as_sensitive": "閲覧注意としてマーク",
"collections.mark_as_sensitive_hint": "コレクションの説明とアカウントを閲覧注意で隠します。コレクション名は表示されます。",
"collections.name_length_hint": "40 文字まで",
"collections.new_collection": "新規のコレクション",
"collections.no_collections_yet": "コレクションはまだありません。",
"collections.remove_account": "このアカウントを削除する",
"collections.report_collection": "このコレクションを通報する",
"collections.search_accounts_label": "追加するアカウントを探す…",
"collections.topic_hint": "ハッシュタグを追加して、このコレクションの主なトピックを知ってもらいましょう。",
"collections.visibility_public": "公開",
"collections.visibility_public_hint": "検索結果やその他おすすめに表示されて、見つけてもらうことができます。",
"collections.visibility_title": "公開範囲",
"collections.visibility_unlisted": "ひかえめな公開",
"collections.visibility_unlisted_hint": "リンクを知っている人が見られます。検索結果やその他おすすめには表示さません。",
"column.about": "概要",
"column.blocks": "ブロックしたユーザー",
"column.bookmarks": "ブックマーク",
@ -461,6 +475,7 @@
"footer.source_code": "ソースコードを表示",
"footer.status": "ステータス",
"footer.terms_of_service": "サービス利用規約",
"form_field.optional": "(省略可能)",
"generic.saved": "保存しました",
"getting_started.heading": "スタート",
"hashtag.admin_moderation": "#{name}のモデレーション画面を開く",

View File

@ -45,6 +45,7 @@
"account.featured": "精選ê",
"account.featured.accounts": "個人資料",
"account.featured.collections": "收藏",
"account.featured.new_collection": "新ê收藏",
"account.field_overflow": "展示規篇內容",
"account.filters.all": "逐ē活動",
"account.filters.boosts_toggle": "顯示轉PO",
@ -102,6 +103,7 @@
"account.muted": "消音ah",
"account.muting": "消音",
"account.mutual": "Lín sio跟tuè",
"account.name.copy": "Khóo-pih口座ê名",
"account.name.help.domain": "",
"account.name.help.domain_self": "{domain} 是管理lí ê個人資料hām PO文ê服侍器。",
"account.name.help.footer": "Tiō親像lí通用別款電子phue程式寄電子phue予別lânglí ē當佇別ê Mastodon服侍器hām別lâng交流koh ē當hām用kap Mastodon kâng款規則ActivityPub 協定)ê別款社里軟體ê lâng交流。",
@ -138,6 +140,9 @@
"account.unmute": "取消消音 @{name}",
"account.unmute_notifications_short": "Kā通知取消消音",
"account.unmute_short": "取消消音",
"account_edit.advanced_settings.bot_hint": "Kā別lâng講tsit ê口座主要行自動操作可能無lâng監控",
"account_edit.advanced_settings.bot_label": "機器lâng ê口座",
"account_edit.advanced_settings.title": "進一步ê設定",
"account_edit.bio.add_label": "加添個人紹介",
"account_edit.bio.edit_label": "編個人紹介",
"account_edit.bio.placeholder": "加一段短紹介幫tsān別lâng認捌lí。",
@ -346,6 +351,7 @@
"closed_registrations_modal.find_another_server": "Tshuē別ê服侍器",
"closed_registrations_modal.preamble": "因為Mastodon非中心化所以bô論tī tá tsi̍t ê服侍器建立口座lí lóng ē當跟tuè tsi̍t ê服侍器ê逐ê lângkap hām in交流。Lí iā ē當ka-tī起tsi̍t ê站!",
"closed_registrations_modal.title": "註冊 Mastodon ê口座",
"collection.share_modal.share_link_label": "分享連結",
"collection.share_modal.share_via_post": "PO佇Mastodon頂",
"collection.share_modal.share_via_system": "分享kàu……",
"collection.share_modal.title": "分享收藏",
@ -426,7 +432,9 @@
"column.list_members": "管理列單ê成員",
"column.lists": "列單",
"column.mutes": "消音ê用者",
"column.my_collections": "我ê收藏",
"column.notifications": "通知",
"column.other_collections": "{name} ê收藏",
"column.pins": "釘起來ê PO文",
"column.public": "聯邦ê時間線",
"column_back_button.label": "頂頁",
@ -496,6 +504,10 @@
"confirmations.follow_to_list.confirm": "跟tuè加入kàu列單",
"confirmations.follow_to_list.message": "Beh kā {name} 加添kàu列單lí tio̍h先跟tuè伊。",
"confirmations.follow_to_list.title": "Kám beh跟tuè tsit ê用者?",
"confirmations.hide_featured_tab.confirm": "Khàm掉分頁",
"confirmations.hide_featured_tab.intro": "Lí不管時ē當佇<i>編輯個人資料→個人資料分頁設定</i>下kha改tse。",
"confirmations.hide_featured_tab.message": "Tse ē kā佇 {serverName} kap別ê pháng上新版本ê Mastodon ê服侍器ê用者khàm掉分頁。其他界面ê顯示可能無kâng款。",
"confirmations.hide_featured_tab.title": "敢beh khàm掉「精選ê」分頁",
"confirmations.logout.confirm": "登出",
"confirmations.logout.message": "Lí kám確定beh登出",
"confirmations.logout.title": "Lí kám beh登出",
@ -575,7 +587,9 @@
"domain_pill.your_server": "Lí數位ê厝內底有lí所有ê PO文。無kah意Ē當轉kàu別ê服侍器koh保有跟tuè lí êl âng。.",
"domain_pill.your_username": "Lí 佇tsit ê服侍器獨一ê稱呼。佇無kâng ê服侍器有可能tshuē著kāng名ê用者。",
"dropdown.empty": "揀選項",
"email_subscriptions.email": "電子phue箱",
"email_subscriptions.form.action": "訂",
"email_subscriptions.form.bottom": "無開Mastodon ê口座mā ē當佇收件箱收著PO文。不管時lóng ē當取消訂。其他資訊,請參考<a>隱私權政策</a>。",
"email_subscriptions.form.title": "訂 {name} ê電子phue更新",
"email_subscriptions.submitted.lead": "請檢查lí ê收件箱來完成訂電子批ê更新。",
"email_subscriptions.submitted.title": "上尾步",

View File

@ -85,7 +85,7 @@
"account.menu.copy": "Link kopiëren",
"account.menu.direct": "Privébericht",
"account.menu.hide_reblogs": "Boosts op tijdlijn verbergen",
"account.menu.mention": "Vermelding",
"account.menu.mention": "Vermelden",
"account.menu.mute": "Account negeren",
"account.menu.note.description": "Alleen voor jou zichtbaar",
"account.menu.open_original_page": "Op {domain} bekijken",
@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "Je wordt in deze verzameling uitgelicht",
"collections.edit_details": "Gegevens bewerken",
"collections.error_loading_collections": "Er is een fout opgetreden bij het laden van je verzamelingen.",
"collections.hidden_accounts_description": "Je hebt {count, plural, one {deze gebruiker} other {deze gebruikers}} geblokkeerd",
"collections.hidden_accounts_link": "{count, plural, one {# verborgen account} other {# verborgen accounts}}",
"collections.hints.accounts_counter": "{count} / {max} accounts",
"collections.last_updated_at": "Laatst bijgewerkt: {date}",
"collections.manage_accounts": "Accounts beheren",

View File

@ -383,6 +383,8 @@
"collections.detail.you_are_in_this_collection": "Shfaqeni te ky koleksion",
"collections.edit_details": "Përpunoni hollësi",
"collections.error_loading_collections": "Pati një gabim teksa provohej të ngarkoheshin koleksionet tuaj.",
"collections.hidden_accounts_description": "Bllokuat, ose heshtuat {count, plural, one {këtë përdorues} other {këta përdorues}}",
"collections.hidden_accounts_link": "{count, plural, one {# llogari e fshehur} other {# llogari të fshehura}}",
"collections.hints.accounts_counter": "{count} / {max} llogari",
"collections.last_updated_at": "Përditësuar së fundi më: {date}",
"collections.manage_accounts": "Administroni llogari",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "你被添加到了此收藏列表",
"collections.edit_details": "编辑详情",
"collections.error_loading_collections": "加载你的收藏列表时发生错误。",
"collections.hidden_accounts_description": "你已屏蔽或隐藏{count, plural, other {部分用户}}",
"collections.hidden_accounts_link": "{count, plural, other {# 个隐藏账号}}",
"collections.hints.accounts_counter": "{count} / {max} 个账号",
"collections.last_updated_at": "最后更新:{date}",
"collections.manage_accounts": "管理账户",

View File

@ -388,6 +388,8 @@
"collections.detail.you_are_in_this_collection": "您已被加入至此收藏名單",
"collections.edit_details": "編輯詳細資料",
"collections.error_loading_collections": "讀取您的收藏名單時發生錯誤。",
"collections.hidden_accounts_description": "您已封鎖或靜音{count, plural, other {這些使用者}}",
"collections.hidden_accounts_link": "{count, plural, other {# 個隱藏帳號}}",
"collections.hints.accounts_counter": "{count} / {max} 個帳號",
"collections.last_updated_at": "最後更新:{date}",
"collections.manage_accounts": "管理帳號",

View File

@ -18,6 +18,7 @@ module Account::Associations
has_many :collections
has_many :collection_items
has_many :curated_collection_items, through: :collections, class_name: 'CollectionItem', source: :collection_items
has_many :featured_in_collections, through: :collection_items, class_name: 'Collection', source: :collection
has_many :conversations, class_name: 'AccountConversation'
has_many :custom_filters
has_many :favourites

View File

@ -72,4 +72,8 @@ class AccountPolicy < ApplicationPolicy
def index_collections?
current_account.nil? || !record.blocking_or_domain_blocking?(current_account)
end
def index_featured_in_collections?
current_account.id == record.id
end
end

View File

@ -337,13 +337,13 @@ de:
notification_emails:
appeal: Jemand hat Einspruch gegen eine Maßnahme erhoben
digest: Zusammenfassung senden
favourite: wenn jemand meinen Beitrag favorisiert
favourite: Jemand favorisierte meinen Beitrag
follow: Ein neues Profil folgt mir
follow_request: Ein Profil fragt an, mir zu folgen
mention: Ich wurde erwähnt
follow_request: Jemand möchte mir folgen
mention: Jemand erwähnte mich
pending_account: Ein neues Konto muss überprüft werden
quote: Jemand zitierte meinen Beitrag
reblog: wenn jemand meinen Beitrag teilt
reblog: Jemand teilte meinen Beitrag
report: Eine neue Meldung wurde eingereicht
software_updates:
all: Über alle Updates informieren

View File

@ -4,7 +4,7 @@ es-AR:
hints:
account:
attribution_domains: Uno por línea. Protege de falsas atribuciones.
discoverable: Puede que aparezcas en colecciones creadas por otros usuarios. También pueden sugerirse tu perfil y publicaciones públicas a otros usuarios en otras funciones de descubrimiento de Mastodon.
discoverable: Puede que aparezcas en colecciones creadas por otros usuarios. También pueden sugerirse tu perfil y publicaciones a otros usuarios en otras funciones de descubrimiento de Mastodon.
display_name: Tu nombre completo o tu pseudónimo.
fields: Tu sitio web, pronombres, edad, o lo que quieras.
indexable: Tus mensajes públicos pueden aparecer en los resultados de la búsqueda en Mastodon. La gente que interactuó con tus mensajes puede ser capaz de buscarlos sin importar el momento.

View File

@ -8,6 +8,7 @@ namespace :api, format: false do
namespace :v1_alpha do
resources :accounts, only: [] do
resources :collections, only: [:index]
resources :in_collections, only: [:index]
end
resources :async_refreshes, only: :show

191
publiccode.yml Normal file
View File

@ -0,0 +1,191 @@
publiccodeYmlVersion: 0.5.0
name: Mastodon
url: https://github.com/mastodon/mastodon
landingURL: https://joinmastodon.org
softwareVersion: v4.5.8
releaseDate: 2026-03-24
logo: app/javascript/images/logo.svg
platforms:
- linux
- windows
- web
- mac
- ios
- android
categories:
- communications
- marketing
- online-community
- social-media-management
organisation:
uri: https://joinmastodon.org
name: Mastodon GmbH
usedBy:
- European Commission
- German Federal Government
- Dutch Government
roadmap: https://joinmastodon.org/roadmap
developmentStatus: stable
softwareType: standalone/backend
intendedAudience:
countries:
- AT
- BE
- BG
- CY
- CZ
- DE
- DK
- EE
- ES
- FI
- FR
- GR
- HR
- HU
- IE
- IT
- LT
- LU
- LV
- MT
- NL
- PL
- PT
- RO
- SE
- SI
- SK
scope:
- society
- government
- education
- culture
description:
en:
localisedName: Mastodon
shortDescription: Connecting the world through thriving online communities
longDescription:
'Mastodon is a free, open-source social network server based on
ActivityPub where users can follow friends and discover new ones. On
Mastodon, users can publish anything they want: links, pictures, text, and
video. All Mastodon servers are interoperable as a federated network
(users on one server can seamlessly communicate with users from another
one, including non-Mastodon software that implements ActivityPub!)'
documentation: https://docs.joinmastodon.org
features:
- Microblogging
- Multimedia (Images, Video, Audio)
- Polls
- Public profiles
- RSS feeds
- User roles and permissions
- Two factor authentication
legal:
license: AGPL-3.0-or-later
maintenance:
type: internal
contacts:
- name: Eugen Rochko
email: eugen@joinmastodon.org
affiliation: Mastodon GmbH
localisation:
localisationReady: true
availableLanguages:
- af
- an
- ar
- ast
- be
- bg
- bn
- br
- bs
- ca
- ckb
- co
- cs
- cy
- da
- de
- el
- en
- en-GB
- eo
- es
- es-AR
- es-MX
- et
- eu
- fa
- fi
- fo
- fr
- fr-CA
- fy
- ga
- gd
- gl
- he
- hi
- hr
- hu
- hy
- ia
- id
- ie
- ig
- io
- is
- it
- ja
- ka
- kab
- kk
- kn
- ko
- ku
- kw
- la
- lt
- lv
- mk
- ml
- mr
- ms
- my
- nan-TW
- nl
- nn
- 'no'
- oc
- pa
- pl
- pt-BR
- pt-PT
- ro
- ru
- sa
- sc
- sco
- si
- sk
- sl
- sq
- sr
- sr-Latn
- sv
- szl
- ta
- te
- th
- tr
- tt
- ug
- uk
- ur
- vi
- zgh
- zh-CN
- zh-HK
- zh-TW

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Api::V1Alpha::InCollections', feature: :collections do
include_context 'with API authentication', oauth_scopes: 'read:collections write:collections'
describe 'GET /api/v1_alpha/in_collections' do
subject do
get "/api/v1_alpha/accounts/#{account.id}/in_collections", headers: headers, params: params
end
let(:params) { {} }
let(:account) { user.account }
before { Fabricate.times(3, :collection_item, account: account) }
it 'returns all collections for the given account and http success' do
subject
expect(response).to have_http_status(200)
expect(response.parsed_body[:collections].size).to eq 3
end
context 'with limit param' do
let(:params) { { limit: '1' } }
it 'returns only a single result' do
subject
expect(response).to have_http_status(200)
expect(response.parsed_body[:collections].size).to eq 1
expect(response)
.to include_pagination_headers(
next: api_v1_alpha_account_in_collections_url(account, limit: 1, offset: 1)
)
end
end
context 'with limit and offset params' do
let(:params) { { limit: '1', offset: '1' } }
it 'returns the correct result and headers' do
subject
expect(response).to have_http_status(200)
expect(response.parsed_body[:collections].size).to eq 1
expect(response)
.to include_pagination_headers(
prev: api_v1_alpha_account_in_collections_url(account, limit: 1, offset: 0),
next: api_v1_alpha_account_in_collections_url(account, limit: 1, offset: 2)
)
end
end
context 'when requested account is different from current account' do
let(:account) { Fabricate(:account) }
it 'returns http forbidden' do
subject
expect(response)
.to have_http_status(403)
end
end
end
end