Merge pull request #3349 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes up to 918563704f5ff5eeb1efa31a412ac38f97781e9e
This commit is contained in:
Claire 2026-01-16 23:19:23 +01:00 committed by GitHub
commit 2c421cfd9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 98 additions and 42 deletions

View File

@ -29,16 +29,17 @@ module ThemeHelper
end
end
def theme_color_tags(flavour_and_skin)
_, theme = flavour_and_skin
if theme == 'system'
def theme_color_tags(color_scheme)
case color_scheme
when 'auto'
''.html_safe.tap do |tags|
tags << tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:dark], media: '(prefers-color-scheme: dark)')
tags << tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:light], media: '(prefers-color-scheme: light)')
end
else
tag.meta name: 'theme-color', content: theme_color_for(theme)
when 'light'
tag.meta name: 'theme-color', content: Themes::THEME_COLORS[:light]
when 'dark'
tag.meta name: 'theme-color', content: Themes::THEME_COLORS[:dark]
end
end
@ -68,8 +69,4 @@ module ThemeHelper
Setting.custom_css&.then { |content| Digest::SHA256.hexdigest(content) }
end
end
def theme_color_for(theme)
theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark]
end
end

View File

@ -6,15 +6,17 @@ import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
const DIRECTORY_FETCH_LIMIT = 20;
export const fetchDirectory = createDataLoadingThunk(
'directory/fetch',
async (params: Parameters<typeof apiGetDirectory>[0]) =>
apiGetDirectory(params),
apiGetDirectory(params, DIRECTORY_FETCH_LIMIT),
(data, { dispatch }) => {
dispatch(importFetchedAccounts(data));
dispatch(fetchRelationships(data.map((x) => x.id)));
return { accounts: data };
return { accounts: data, isLast: data.length < DIRECTORY_FETCH_LIMIT };
},
);
@ -26,12 +28,15 @@ export const expandDirectory = createDataLoadingThunk(
'items',
]) as ImmutableList<unknown>;
return apiGetDirectory({ ...params, offset: loadedItems.size }, 20);
return apiGetDirectory(
{ ...params, offset: loadedItems.size },
DIRECTORY_FETCH_LIMIT,
);
},
(data, { dispatch }) => {
dispatch(importFetchedAccounts(data));
dispatch(fetchRelationships(data.map((x) => x.id)));
return { accounts: data };
return { accounts: data, isLast: data.length < DIRECTORY_FETCH_LIMIT };
},
);

View File

@ -86,6 +86,9 @@ export const Directory: React.FC<{
(state) =>
state.user_lists.getIn(['directory', 'isLoading'], true) as boolean,
);
const hasMore = useAppSelector(
(state) => !!state.user_lists.getIn(['directory', 'next']),
);
useEffect(() => {
void dispatch(fetchDirectory({ order, local }));
@ -185,7 +188,7 @@ export const Directory: React.FC<{
<LoadMore
onClick={handleLoadMore}
visible={!initialLoad}
visible={!initialLoad && hasMore}
loading={isLoading}
/>
</div>

View File

@ -204,9 +204,9 @@ export default function userLists(state = initialState, action) {
else if (fetchFeaturedTags.rejected.match(action))
return state.setIn(['featured_tags', action.meta.arg.accountId, 'isLoading'], false);
else if (fetchDirectory.fulfilled.match(action))
return normalizeList(state, ['directory'], action.payload.accounts, undefined);
return normalizeList(state, ['directory'], action.payload.accounts, action.payload.isLast ? null : true);
else if (expandDirectory.fulfilled.match(action))
return appendToList(state, ['directory'], action.payload.accounts, undefined);
return appendToList(state, ['directory'], action.payload.accounts, action.payload.isLast ? null : true);
else if (fetchDirectory.pending.match(action) ||
expandDirectory.pending.match(action))
return state.setIn(['directory', 'isLoading'], true);

View File

@ -6,15 +6,17 @@ import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
const DIRECTORY_FETCH_LIMIT = 20;
export const fetchDirectory = createDataLoadingThunk(
'directory/fetch',
async (params: Parameters<typeof apiGetDirectory>[0]) =>
apiGetDirectory(params),
apiGetDirectory(params, DIRECTORY_FETCH_LIMIT),
(data, { dispatch }) => {
dispatch(importFetchedAccounts(data));
dispatch(fetchRelationships(data.map((x) => x.id)));
return { accounts: data };
return { accounts: data, isLast: data.length < DIRECTORY_FETCH_LIMIT };
},
);
@ -26,12 +28,15 @@ export const expandDirectory = createDataLoadingThunk(
'items',
]) as ImmutableList<unknown>;
return apiGetDirectory({ ...params, offset: loadedItems.size }, 20);
return apiGetDirectory(
{ ...params, offset: loadedItems.size },
DIRECTORY_FETCH_LIMIT,
);
},
(data, { dispatch }) => {
dispatch(importFetchedAccounts(data));
dispatch(fetchRelationships(data.map((x) => x.id)));
return { accounts: data };
return { accounts: data, isLast: data.length < DIRECTORY_FETCH_LIMIT };
},
);

View File

@ -83,6 +83,9 @@ export const Directory: React.FC<{
(state) =>
state.user_lists.getIn(['directory', 'isLoading'], true) as boolean,
);
const hasMore = useAppSelector(
(state) => !!state.user_lists.getIn(['directory', 'next']),
);
useEffect(() => {
void dispatch(fetchDirectory({ order, local }));
@ -182,7 +185,7 @@ export const Directory: React.FC<{
<LoadMore
onClick={handleLoadMore}
visible={!initialLoad}
visible={!initialLoad && hasMore}
loading={isLoading}
/>
</div>

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# новы элемент} few {# новыя элементы} many {# новых элементаў} other {# новых элементаў}}",
"loading_indicator.label": "Ідзе загрузка…",
"media_gallery.hide": "Схаваць",
"minicard.more_items": "+ яшчэ {count}",
"moved_to_account_banner.text": "Ваш уліковы запіс {disabledAccount} зараз адключаны, таму што Вы перайшлі на {movedToAccount}.",
"mute_modal.hide_from_notifications": "Схаваць з апавяшчэнняў",
"mute_modal.hide_options": "Схаваць опцыі",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# nyt element} other {# nye elementer}}",
"loading_indicator.label": "Indlæser…",
"media_gallery.hide": "Skjul",
"minicard.more_items": "+ {count} mere",
"moved_to_account_banner.text": "Din konto {disabledAccount} er i øjeblikket deaktiveret, fordi du er flyttet til {movedToAccount}.",
"mute_modal.hide_from_notifications": "Skjul fra notifikationer",
"mute_modal.hide_options": "Skjul valgmuligheder",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}",
"loading_indicator.label": "Lädt …",
"media_gallery.hide": "Ausblenden",
"minicard.more_items": "+ {count} weitere",
"moved_to_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert, weil du zu {movedToAccount} umgezogen bist.",
"mute_modal.hide_from_notifications": "Auch aus den Benachrichtigungen entfernen",
"mute_modal.hide_options": "Optionen ausblenden",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# νέο στοιχείο} other {# νέα στοιχεία}}",
"loading_indicator.label": "Φόρτωση…",
"media_gallery.hide": "Απόκρυψη",
"minicard.more_items": "+ {count} ακόμα",
"moved_to_account_banner.text": "Ο λογαριασμός σου {disabledAccount} είναι προσωρινά απενεργοποιημένος επειδή μεταφέρθηκες στον {movedToAccount}.",
"mute_modal.hide_from_notifications": "Απόκρυψη από ειδοποιήσεις",
"mute_modal.hide_options": "Απόκρυψη επιλογών",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# new item} other {# new items}}",
"loading_indicator.label": "Loading…",
"media_gallery.hide": "Hide",
"minicard.more_items": "+ {count} more",
"moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
"mute_modal.hide_from_notifications": "Hide from notifications",
"mute_modal.hide_options": "Hide options",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# elemento nuevo} other {# elementos nuevos}}",
"loading_indicator.label": "Cargando…",
"media_gallery.hide": "Ocultar",
"minicard.more_items": "y {count} más",
"moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te mudaste a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ocultar en las notificaciones",
"mute_modal.hide_options": "Ocultar opciones",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}",
"loading_indicator.label": "Cargando…",
"media_gallery.hide": "Ocultar",
"minicard.more_items": "y {count} más",
"moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te has mudado a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ocultar de las notificaciones",
"mute_modal.hide_options": "Ocultar opciones",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# nuevo elemento} other {# nuevos elementos}}",
"loading_indicator.label": "Cargando…",
"media_gallery.hide": "Ocultar",
"minicard.more_items": "y {count} más",
"moved_to_account_banner.text": "Tu cuenta {disabledAccount} está actualmente deshabilitada porque te has mudado a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ocultar de las notificaciones",
"mute_modal.hide_options": "Ocultar opciones",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# nýtt evni} other {# nýggj evni}}",
"loading_indicator.label": "Innlesur…",
"media_gallery.hide": "Fjal",
"minicard.more_items": "+ {count} meira",
"moved_to_account_banner.text": "Konta tín {disabledAccount} er í løtuni óvirkin, tí tú flutti til {movedToAccount}.",
"mute_modal.hide_from_notifications": "Fjal boð",
"mute_modal.hide_options": "Fjal valmøguleikar",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# új elem} other {# új elem}}",
"loading_indicator.label": "Betöltés…",
"media_gallery.hide": "Elrejtés",
"minicard.more_items": "+ {count} további",
"moved_to_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva, mert átköltöztél ide: {movedToAccount}.",
"mute_modal.hide_from_notifications": "Elrejtés az értesítések közül",
"mute_modal.hide_options": "Beállítások elrejtése",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# nýtt atriði} other {# ný atriði}}",
"loading_indicator.label": "Hleð inn…",
"media_gallery.hide": "Fela",
"minicard.more_items": "+ {count} í viðbót",
"moved_to_account_banner.text": "Aðgangurinn þinn {disabledAccount} er óvirkur í augnablikinu vegna þess að þú fluttir þig yfir á {movedToAccount}.",
"mute_modal.hide_from_notifications": "Fela úr tilkynningum",
"mute_modal.hide_options": "Fela valkosti",

View File

@ -69,7 +69,7 @@
"account.mute_short": "Nutildyti",
"account.muted": "Nutildytas",
"account.muting": "Užtildymas",
"account.mutual": "Jūs sekate vienas kitą",
"account.mutual": "Sekate vienas kitą",
"account.no_bio": "Nėra pateikto aprašymo.",
"account.open_original_page": "Atidaryti originalų puslapį",
"account.posts": "Įrašai",
@ -685,7 +685,7 @@
"notifications.filter.follows": "Sekimai",
"notifications.filter.mentions": "Paminėjimai",
"notifications.filter.polls": "Balsavimo rezultatai",
"notifications.filter.statuses": "Naujinimai iš žmonių, kuriuos seki",
"notifications.filter.statuses": "Naujienos iš žmonių, kuriuos sekate",
"notifications.grant_permission": "Suteikti leidimą.",
"notifications.group": "{count} pranešimai",
"notifications.mark_as_read": "Pažymėti kiekvieną pranešimą kaip perskaitytą",
@ -821,7 +821,7 @@
"report.thanks.title": "Nenori to matyti?",
"report.thanks.title_actionable": "Ačiū, kad pranešei, mes tai išnagrinėsime.",
"report.unfollow": "Nebesekti @{name}",
"report.unfollow_explanation": "Tu seki šią paskyrą. Jei nori nebematyti jų įrašų savo pagrindiniame sraute, nebesek jų.",
"report.unfollow_explanation": "Jūs sekate šią paskyrą. Kad nebematytumėte jų įrašų savo pagrindiniame sraute, nebesekite.",
"report_notification.attached_statuses": "Pridėti {count, plural, one {{count} įrašas} few {{count} įrašai} many {{count} įrašo} other {{count} įrašų}}",
"report_notification.categories.legal": "Teisinės",
"report_notification.categories.legal_sentence": "nelegalus turinys",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# novo item} other {# novos itens}}",
"loading_indicator.label": "A carregar…",
"media_gallery.hide": "Esconder",
"minicard.more_items": "+ {count} mais",
"moved_to_account_banner.text": "A tua conta {disabledAccount} está neste momento desativada porque migraste para {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ocultar das notificações",
"mute_modal.hide_options": "Ocultar opções",

View File

@ -587,6 +587,7 @@
"load_pending": "{count, plural,one {# objekt i ri }other {# objekte të rinj }}",
"loading_indicator.label": "Po ngarkohet…",
"media_gallery.hide": "Fshihe",
"minicard.more_items": "+ {count} më tepër",
"moved_to_account_banner.text": "Llogaria juaj {disabledAccount} aktualisht është e çaktivizuar, ngaqë kaluat te {movedToAccount}.",
"mute_modal.hide_from_notifications": "Fshihe prej njoftimeve",
"mute_modal.hide_options": "Fshihi mundësitë",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, one {# tút mới} other {# tút mới}}",
"loading_indicator.label": "Đang tải…",
"media_gallery.hide": "Ẩn",
"minicard.more_items": "+ {count} khác",
"moved_to_account_banner.text": "Tài khoản {disabledAccount} của bạn hiện không khả dụng vì bạn đã chuyển sang {movedToAccount}.",
"mute_modal.hide_from_notifications": "Ẩn thông báo",
"mute_modal.hide_options": "Ẩn tùy chọn",

View File

@ -589,6 +589,7 @@
"load_pending": "{count} 项",
"loading_indicator.label": "加载中…",
"media_gallery.hide": "隐藏",
"minicard.more_items": "+ 还有 {count} 项",
"moved_to_account_banner.text": "你的账号 {disabledAccount} 已禁用,因为你已迁移到 {movedToAccount}。",
"mute_modal.hide_from_notifications": "从通知中隐藏",
"mute_modal.hide_options": "隐藏选项",

View File

@ -589,6 +589,7 @@
"load_pending": "{count, plural, other {# 個新項目}}",
"loading_indicator.label": "正在載入...",
"media_gallery.hide": "隱藏",
"minicard.more_items": "+ 其餘 {count} 個",
"moved_to_account_banner.text": "您的帳號 {disabledAccount} 目前已停用,因為您已搬家至 {movedToAccount}。",
"mute_modal.hide_from_notifications": "於推播通知中隱藏",
"mute_modal.hide_options": "隱藏選項",

View File

@ -204,9 +204,9 @@ export default function userLists(state = initialState, action) {
else if (fetchFeaturedTags.rejected.match(action))
return state.setIn(['featured_tags', action.meta.arg.accountId, 'isLoading'], false);
else if (fetchDirectory.fulfilled.match(action))
return normalizeList(state, ['directory'], action.payload.accounts, undefined);
return normalizeList(state, ['directory'], action.payload.accounts, action.payload.isLast ? null : true);
else if (expandDirectory.fulfilled.match(action))
return appendToList(state, ['directory'], action.payload.accounts, undefined);
return appendToList(state, ['directory'], action.payload.accounts, action.payload.isLast ? null : true);
else if (fetchDirectory.pending.match(action) ||
expandDirectory.pending.match(action))
return state.setIn(['directory', 'isLoading'], true);

View File

@ -21,7 +21,7 @@
%link{ rel: 'mask-icon', href: frontend_asset_path('images/logo-symbol-icon.svg'), color: '#6364FF' }/
%link{ rel: 'manifest', href: manifest_path(format: :json) }/
= javascript_inline_tag 'theme-selection.js'
= theme_color_tags current_theme
= theme_color_tags color_scheme
%meta{ name: 'mobile-web-app-capable', content: 'yes' }/
%title= html_title

View File

@ -1,5 +1,5 @@
!!! 5
%html{ lang: I18n.locale }
%html{ lang: I18n.locale, 'data-contrast': 'auto', 'data-color-scheme': 'light' }
%head
%meta{ charset: 'utf-8' }/
%meta{ name: 'robots', content: 'noindex' }/
@ -11,6 +11,7 @@
- if storage_host?
%link{ rel: 'dns-prefetch', href: storage_host }/
= javascript_inline_tag 'theme-selection.js'
= vite_client_tag
= vite_react_refresh_tag
= vite_polyfills_tag

View File

@ -904,6 +904,7 @@ cy:
publish_statistics: Cyhoeddi ystadegau
title: Darganfod
trends: Tueddiadau
wrapstodon: Wrapstodon
domain_blocks:
all: I bawb
disabled: I neb

View File

@ -1033,7 +1033,7 @@ lt:
merge_long: Išsaugoti esančius įrašus ir pridėti naujus
overwrite: Perrašyti
overwrite_long: Pakeisti senus įrašus naujais
preface: Gali importuoti duomenis, kuriuos eksportavai iš kito serverio, pavyzdžiui, sekamų arba blokuojamų žmonių sąrašą.
preface: Galite importuoti duomenis, kuriuos eksportavote iš kito serverio kaip sekamų arba blokuojamų žmonių sąrašą.
success: Jūsų informacija sėkmingai įkelta ir bus apdorota kaip įmanoma greičiau
types:
blocking: Blokuojamų sąrašas

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class UseSnowflakeIdsForCollections < ActiveRecord::Migration[8.0]
def up
safety_assured do
execute(<<~SQL.squish)
ALTER TABLE collections ALTER COLUMN id SET DEFAULT timestamp_id('collections');
ALTER TABLE collection_items ALTER COLUMN id SET DEFAULT timestamp_id('collection_items');
SQL
end
Mastodon::Snowflake.ensure_id_sequences_exist
end
def down
execute(<<~SQL.squish)
LOCK collections;
SELECT setval('collections_id_seq', (SELECT MAX(id) FROM collections));
ALTER TABLE collections ALTER COLUMN id SET DEFAULT nextval('collections_id_seq');
LOCK collection_items;
SELECT setval('collection_items_id_seq', (SELECT MAX(id) FROM collection_items));
ALTER TABLE collection_items ALTER COLUMN id SET DEFAULT nextval('collection_items_id_seq');
SQL
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_12_17_091936) do
ActiveRecord::Schema[8.0].define(version: 2026_01_15_153219) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -352,7 +352,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_17_091936) do
t.index ["reference_account_id"], name: "index_canonical_email_blocks_on_reference_account_id"
end
create_table "collection_items", force: :cascade do |t|
create_table "collection_items", id: :bigint, default: -> { "timestamp_id('collection_items'::text)" }, force: :cascade do |t|
t.bigint "collection_id", null: false
t.bigint "account_id"
t.integer "position", default: 1, null: false
@ -369,7 +369,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_17_091936) do
t.index ["object_uri"], name: "index_collection_items_on_object_uri", unique: true, where: "(activity_uri IS NOT NULL)"
end
create_table "collections", force: :cascade do |t|
create_table "collections", id: :bigint, default: -> { "timestamp_id('collections'::text)" }, force: :cascade do |t|
t.bigint "account_id", null: false
t.string "name", null: false
t.text "description", null: false

View File

@ -49,12 +49,12 @@ RSpec.describe ThemeHelper do
end
describe 'theme_color_tags' do
let(:result) { helper.theme_color_tags(theme) }
let(:result) { helper.theme_color_tags(color_scheme) }
context 'when using system theme' do
let(:theme) { ['glitch', 'system'] }
let(:color_scheme) { 'auto' }
it 'returns the mastodon-light and default stylesheets with correct color schemes' do
it 'returns both color schemes with appropriate media queries' do
expect(html_theme_colors.first.attributes.symbolize_keys)
.to include(
content: have_attributes(value: Themes::THEME_COLORS[:dark]),
@ -68,10 +68,10 @@ RSpec.describe ThemeHelper do
end
end
context 'when using mastodon-light theme' do
let(:theme) { ['glitch', 'mastodon-light'] }
context 'when light color scheme' do
let(:color_scheme) { 'light' }
it 'returns the theme stylesheet without color scheme information' do
it 'returns the light color' do
expect(html_theme_colors.first.attributes.symbolize_keys)
.to include(
content: have_attributes(value: Themes::THEME_COLORS[:light])
@ -79,10 +79,10 @@ RSpec.describe ThemeHelper do
end
end
context 'when using other theme' do
let(:theme) { ['glitch', 'contrast'] }
context 'when using dark color scheme' do
let(:color_scheme) { 'dark' }
it 'returns the theme stylesheet without color scheme information' do
it 'returns the dark color' do
expect(html_theme_colors.first.attributes.symbolize_keys)
.to include(
content: have_attributes(value: Themes::THEME_COLORS[:dark])