From fe4613ba325f0dd07176b6a7519a4e36d0f59a54 Mon Sep 17 00:00:00 2001 From: diondiondion Date: Thu, 4 Jun 2026 16:28:51 +0200 Subject: [PATCH] Display collection in search results when searched by URL (#39289) --- app/javascript/mastodon/actions/search.ts | 19 +++++++++-- .../mastodon/features/search/index.tsx | 32 ++++++++++++++++++- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/models/search.ts | 7 ++-- app/javascript/mastodon/reducers/search.ts | 3 ++ .../mastodon/reducers/slices/collections.ts | 13 +++++++- 6 files changed, 69 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/actions/search.ts b/app/javascript/mastodon/actions/search.ts index 4f21a53b4d..4b97682a74 100644 --- a/app/javascript/mastodon/actions/search.ts +++ b/app/javascript/mastodon/actions/search.ts @@ -12,6 +12,11 @@ import { createAppAsyncThunk, } from 'mastodon/store/typed_functions'; +import { + importAccountsForPreviewCard, + importFetchedCollections, +} from '../reducers/slices/collections'; + import { fetchRelationships } from './accounts'; import { importFetchedAccounts, importFetchedStatuses } from './importer'; @@ -29,7 +34,7 @@ export const submitSearch = createDataLoadingThunk( limit: 11, }); }, - (data, { dispatch }) => { + async (data, { dispatch }) => { if (data.accounts.length > 0) { dispatch(importFetchedAccounts(data.accounts)); dispatch(fetchRelationships(data.accounts.map((account) => account.id))); @@ -39,6 +44,11 @@ export const submitSearch = createDataLoadingThunk( dispatch(importFetchedStatuses(data.statuses)); } + if (data.collections.length > 0) { + dispatch(importFetchedCollections(data.collections)); + await importAccountsForPreviewCard(data.collections, dispatch); + } + return data; }, { @@ -60,7 +70,7 @@ export const expandSearch = createDataLoadingThunk( offset, }); }, - (data, { dispatch }) => { + async (data, { dispatch }) => { if (data.accounts.length > 0) { dispatch(importFetchedAccounts(data.accounts)); dispatch(fetchRelationships(data.accounts.map((account) => account.id))); @@ -70,6 +80,11 @@ export const expandSearch = createDataLoadingThunk( dispatch(importFetchedStatuses(data.statuses)); } + if (data.collections.length > 0) { + dispatch(importFetchedCollections(data.collections)); + await importAccountsForPreviewCard(data.collections, dispatch); + } + return data; }, { diff --git a/app/javascript/mastodon/features/search/index.tsx b/app/javascript/mastodon/features/search/index.tsx index 724d9c16d6..8ff5c9016a 100644 --- a/app/javascript/mastodon/features/search/index.tsx +++ b/app/javascript/mastodon/features/search/index.tsx @@ -4,6 +4,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { Helmet } from '@unhead/react/helmet'; +import CollectionsIcon from '@/material-icons/400-24px/category.svg?react'; import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react'; @@ -23,6 +24,8 @@ import { useSearchParam } from 'mastodon/hooks/useSearchParam'; import type { Hashtag as HashtagType } from 'mastodon/models/tags'; import { useAppDispatch, useAppSelector } from 'mastodon/store'; +import { CollectionListItem } from '../collections/components/collection_list_item'; + import { SearchSection } from './components/search_section'; const messages = defineMessages({ @@ -131,7 +134,8 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({ filteredResults = results.accounts.length + results.hashtags.length + - results.statuses.length > + results.statuses.length + + results.collections.length > 0 ? ( <> {results.accounts.length > 0 && ( @@ -154,6 +158,32 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({ )} + {results.collections.length > 0 && ( + + + + + } + > + {results.collections + .slice(0, INITIAL_DISPLAY) + .map((collection, index, array) => ( + + ))} + + )} + {results.hashtags.length > 0 && ( ({ accounts: serverJSON.accounts.map((account) => account.id), statuses: serverJSON.statuses.map((status) => status.id), hashtags: serverJSON.hashtags, + collections: serverJSON.collections, }); diff --git a/app/javascript/mastodon/reducers/search.ts b/app/javascript/mastodon/reducers/search.ts index c822990244..7b9e1f6fcf 100644 --- a/app/javascript/mastodon/reducers/search.ts +++ b/app/javascript/mastodon/reducers/search.ts @@ -49,6 +49,9 @@ export const searchReducer = createReducer(initialState, (builder) => { hashtags: state.results ? [...state.results.hashtags, ...results.hashtags] : results.hashtags, + collections: state.results + ? [...state.results.collections, ...results.collections] + : results.collections, }; state.loading = false; }); diff --git a/app/javascript/mastodon/reducers/slices/collections.ts b/app/javascript/mastodon/reducers/slices/collections.ts index 56cf907f78..41bda5aa95 100644 --- a/app/javascript/mastodon/reducers/slices/collections.ts +++ b/app/javascript/mastodon/reducers/slices/collections.ts @@ -121,6 +121,15 @@ const collectionSlice = createSlice({ const { field, value } = action.payload; state.editor[field] = value; }, + importFetchedCollections( + state, + action: PayloadAction, + ) { + const collections = action.payload; + collections.forEach((collection) => { + state.collections[collection.id] = collection; + }); + }, }, extraReducers(builder) { /** @@ -328,7 +337,7 @@ const collectionSlice = createSlice({ /** * Prefetch accounts whose avatars will be displayed in the collection list */ -async function importAccountsForPreviewCard( +export async function importAccountsForPreviewCard( collections: ApiCollectionJSON[], dispatch: AppDispatch, ) { @@ -419,6 +428,8 @@ export const collections = collectionSlice.reducer; export const collectionEditorActions = collectionSlice.actions; export const updateCollectionEditorField = collectionSlice.actions.updateEditorField; +export const importFetchedCollections = + collectionSlice.actions.importFetchedCollections; /** * Selectors