Display collection in search results when searched by URL (#39289)

This commit is contained in:
diondiondion 2026-06-04 16:28:51 +02:00 committed by GitHub
parent 085c91bf12
commit fe4613ba32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 69 additions and 6 deletions

View File

@ -12,6 +12,11 @@ import {
createAppAsyncThunk, createAppAsyncThunk,
} from 'mastodon/store/typed_functions'; } from 'mastodon/store/typed_functions';
import {
importAccountsForPreviewCard,
importFetchedCollections,
} from '../reducers/slices/collections';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer'; import { importFetchedAccounts, importFetchedStatuses } from './importer';
@ -29,7 +34,7 @@ export const submitSearch = createDataLoadingThunk(
limit: 11, limit: 11,
}); });
}, },
(data, { dispatch }) => { async (data, { dispatch }) => {
if (data.accounts.length > 0) { if (data.accounts.length > 0) {
dispatch(importFetchedAccounts(data.accounts)); dispatch(importFetchedAccounts(data.accounts));
dispatch(fetchRelationships(data.accounts.map((account) => account.id))); dispatch(fetchRelationships(data.accounts.map((account) => account.id)));
@ -39,6 +44,11 @@ export const submitSearch = createDataLoadingThunk(
dispatch(importFetchedStatuses(data.statuses)); dispatch(importFetchedStatuses(data.statuses));
} }
if (data.collections.length > 0) {
dispatch(importFetchedCollections(data.collections));
await importAccountsForPreviewCard(data.collections, dispatch);
}
return data; return data;
}, },
{ {
@ -60,7 +70,7 @@ export const expandSearch = createDataLoadingThunk(
offset, offset,
}); });
}, },
(data, { dispatch }) => { async (data, { dispatch }) => {
if (data.accounts.length > 0) { if (data.accounts.length > 0) {
dispatch(importFetchedAccounts(data.accounts)); dispatch(importFetchedAccounts(data.accounts));
dispatch(fetchRelationships(data.accounts.map((account) => account.id))); dispatch(fetchRelationships(data.accounts.map((account) => account.id)));
@ -70,6 +80,11 @@ export const expandSearch = createDataLoadingThunk(
dispatch(importFetchedStatuses(data.statuses)); dispatch(importFetchedStatuses(data.statuses));
} }
if (data.collections.length > 0) {
dispatch(importFetchedCollections(data.collections));
await importAccountsForPreviewCard(data.collections, dispatch);
}
return data; return data;
}, },
{ {

View File

@ -4,6 +4,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet'; 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 FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.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 type { Hashtag as HashtagType } from 'mastodon/models/tags';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { CollectionListItem } from '../collections/components/collection_list_item';
import { SearchSection } from './components/search_section'; import { SearchSection } from './components/search_section';
const messages = defineMessages({ const messages = defineMessages({
@ -131,7 +134,8 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
filteredResults = filteredResults =
results.accounts.length + results.accounts.length +
results.hashtags.length + results.hashtags.length +
results.statuses.length > results.statuses.length +
results.collections.length >
0 ? ( 0 ? (
<> <>
{results.accounts.length > 0 && ( {results.accounts.length > 0 && (
@ -154,6 +158,32 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
</SearchSection> </SearchSection>
)} )}
{results.collections.length > 0 && (
<SearchSection
key='collections'
title={
<>
<Icon id='collections' icon={CollectionsIcon} />
<FormattedMessage
id='search_results.collections'
defaultMessage='Collections'
/>
</>
}
>
{results.collections
.slice(0, INITIAL_DISPLAY)
.map((collection, index, array) => (
<CollectionListItem
key={collection.id}
collection={collection}
listSize={array.length}
positionInList={index + 1}
/>
))}
</SearchSection>
)}
{results.hashtags.length > 0 && ( {results.hashtags.length > 0 && (
<SearchSection <SearchSection
key='hashtags' key='hashtags'

View File

@ -1199,6 +1199,7 @@
"search_popout.user": "user", "search_popout.user": "user",
"search_results.accounts": "Profiles", "search_results.accounts": "Profiles",
"search_results.all": "All", "search_results.all": "All",
"search_results.collections": "Collections",
"search_results.hashtags": "Hashtags", "search_results.hashtags": "Hashtags",
"search_results.no_results": "No results.", "search_results.no_results": "No results.",
"search_results.no_search_yet": "Try searching for posts, profiles or hashtags.", "search_results.no_search_yet": "Try searching for posts, profiles or hashtags.",

View File

@ -1,5 +1,6 @@
import type { ApiSearchResultsJSON } from 'mastodon/api_types/search'; import type { ApiCollectionJSON } from '@/mastodon/api_types/collections';
import type { ApiHashtagJSON } from 'mastodon/api_types/tags'; import type { ApiSearchResultsJSON } from '@/mastodon/api_types/search';
import type { ApiHashtagJSON } from '@/mastodon/api_types/tags';
export type SearchType = 'account' | 'hashtag' | 'accounts' | 'statuses'; export type SearchType = 'account' | 'hashtag' | 'accounts' | 'statuses';
@ -12,10 +13,12 @@ export interface SearchResults {
accounts: string[]; accounts: string[];
statuses: string[]; statuses: string[];
hashtags: ApiHashtagJSON[]; hashtags: ApiHashtagJSON[];
collections: ApiCollectionJSON[];
} }
export const createSearchResults = (serverJSON: ApiSearchResultsJSON) => ({ export const createSearchResults = (serverJSON: ApiSearchResultsJSON) => ({
accounts: serverJSON.accounts.map((account) => account.id), accounts: serverJSON.accounts.map((account) => account.id),
statuses: serverJSON.statuses.map((status) => status.id), statuses: serverJSON.statuses.map((status) => status.id),
hashtags: serverJSON.hashtags, hashtags: serverJSON.hashtags,
collections: serverJSON.collections,
}); });

View File

@ -49,6 +49,9 @@ export const searchReducer = createReducer(initialState, (builder) => {
hashtags: state.results hashtags: state.results
? [...state.results.hashtags, ...results.hashtags] ? [...state.results.hashtags, ...results.hashtags]
: results.hashtags, : results.hashtags,
collections: state.results
? [...state.results.collections, ...results.collections]
: results.collections,
}; };
state.loading = false; state.loading = false;
}); });

View File

@ -121,6 +121,15 @@ const collectionSlice = createSlice({
const { field, value } = action.payload; const { field, value } = action.payload;
state.editor[field] = value; state.editor[field] = value;
}, },
importFetchedCollections(
state,
action: PayloadAction<ApiCollectionJSON[]>,
) {
const collections = action.payload;
collections.forEach((collection) => {
state.collections[collection.id] = collection;
});
},
}, },
extraReducers(builder) { extraReducers(builder) {
/** /**
@ -328,7 +337,7 @@ const collectionSlice = createSlice({
/** /**
* Prefetch accounts whose avatars will be displayed in the collection list * Prefetch accounts whose avatars will be displayed in the collection list
*/ */
async function importAccountsForPreviewCard( export async function importAccountsForPreviewCard(
collections: ApiCollectionJSON[], collections: ApiCollectionJSON[],
dispatch: AppDispatch, dispatch: AppDispatch,
) { ) {
@ -419,6 +428,8 @@ export const collections = collectionSlice.reducer;
export const collectionEditorActions = collectionSlice.actions; export const collectionEditorActions = collectionSlice.actions;
export const updateCollectionEditorField = export const updateCollectionEditorField =
collectionSlice.actions.updateEditorField; collectionSlice.actions.updateEditorField;
export const importFetchedCollections =
collectionSlice.actions.importFetchedCollections;
/** /**
* Selectors * Selectors