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

This commit is contained in:
Claire committed 2026-03-04 18:40:39 +01:00
commit 753e4bee8d
26 files changed
+656 -114

No files matched your search

@@ -12,6 +12,11 @@ import { Account } from 'mastodon/components/account';
import { ColumnBackButton } from 'mastodon/components/column_back_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { RemoteHint } from 'mastodon/components/remote_hint';
import {
Article,
ItemList,
Scrollable,
} from 'mastodon/components/scrollable_list/components';
import { AccountHeader } from 'mastodon/features/account_timeline/components/account_header';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import Column from 'mastodon/features/ui/components/column';
@@ -115,7 +120,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
<Column>
<ColumnBackButton />
<div className='scrollable scrollable--flex'>
<Scrollable>
{accountId && (
<AccountHeader accountId={accountId} hideTabs={forceEmptyState} />
)}
@@ -127,15 +132,17 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
defaultMessage='Collections'
/>
</h4>
<section>
<ItemList>
{publicCollections.map((item, index) => (
<CollectionListItem
key={item.id}
collection={item}
withoutBorder={index === publicCollections.length - 1}
positionInList={index + 1}
listSize={publicCollections.length}
/>
))}
</section>
</ItemList>
</>
)}
{!featuredTags.isEmpty() && (
@@ -146,9 +153,18 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
defaultMessage='Hashtags'
/>
</h4>
{featuredTags.map((tag) => (
<FeaturedTag key={tag.get('id')} tag={tag} account={acct} />
))}
<ItemList>
{featuredTags.map((tag, index) => (
<Article
focusable
key={tag.get('id')}
aria-posinset={index + 1}
aria-setsize={featuredTags.size}
>
<FeaturedTag tag={tag} account={acct} />
</Article>
))}
</ItemList>
</>
)}
{!featuredAccountIds.isEmpty() && (
@@ -159,13 +175,22 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
defaultMessage='Profiles'
/>
</h4>
{featuredAccountIds.map((featuredAccountId) => (
<Account key={featuredAccountId} id={featuredAccountId} />
))}
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<Account id={featuredAccountId} />
</Article>
))}
</ItemList>
</>
)}
<RemoteHint accountId={accountId} />
</div>
</Scrollable>
</Column>
);
};
@@ -7,6 +7,7 @@ import { Link } from 'react-router-dom';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { Article } from 'mastodon/components/scrollable_list/components';
import classes from './collection_list_item.module.scss';
import { CollectionMenu } from './collection_menu';
@@ -68,19 +69,22 @@ export const CollectionMetaData: React.FC<{
export const CollectionListItem: React.FC<{
collection: ApiCollectionJSON;
withoutBorder?: boolean;
}> = ({ collection, withoutBorder }) => {
positionInList: number;
listSize: number;
}> = ({ collection, withoutBorder, positionInList, listSize }) => {
const { id, name } = collection;
const linkId = useId();
return (
<article
<Article
focusable
className={classNames(
classes.wrapper,
'focusable',
withoutBorder && classes.wrapperWithoutBorder,
)}
tabIndex={-1}
aria-labelledby={linkId}
aria-posinset={positionInList}
aria-setsize={listSize}
>
<div className={classes.content}>
<h2 id={linkId}>
@@ -92,6 +96,6 @@ export const CollectionListItem: React.FC<{
</div>
<CollectionMenu context='list' collection={collection} />
</article>
</Article>
);
};
@@ -19,7 +19,11 @@ import {
LinkedDisplayName,
} from 'mastodon/components/display_name';
import { IconButton } from 'mastodon/components/icon_button';
import ScrollableList from 'mastodon/components/scrollable_list';
import {
Article,
ItemList,
Scrollable,
} from 'mastodon/components/scrollable_list/components';
import { Tag } from 'mastodon/components/tags/tag';
import { useAccount } from 'mastodon/hooks/useAccount';
import { me } from 'mastodon/initial_state';
@@ -202,24 +206,27 @@ export const CollectionDetailPage: React.FC<{
multiColumn={multiColumn}
/>
<ScrollableList
scrollKey='collection-detail'
emptyMessage={intl.formatMessage(messages.empty)}
showLoading={isLoading}
bindToDocument={!multiColumn}
alwaysPrepend
prepend={
collection ? <CollectionHeader collection={collection} /> : null
}
>
{collection?.items.map(({ account_id }) => (
<CollectionAccountItem
key={account_id}
accountId={account_id}
collectionOwnerId={collection.account_id}
/>
))}
</ScrollableList>
<Scrollable>
{collection && <CollectionHeader collection={collection} />}
<ItemList
isLoading={isLoading}
emptyMessage={intl.formatMessage(messages.empty)}
>
{collection?.items.map(({ account_id }, index, items) => (
<Article
key={account_id}
data-id={account_id}
aria-posinset={index + 1}
aria-setsize={items.length}
>
<CollectionAccountItem
accountId={account_id}
collectionOwnerId={collection.account_id}
/>
</Article>
))}
</ItemList>
</Scrollable>
<Helmet>
<title>{pageTitle}</title>
@@ -21,7 +21,11 @@ import { EmptyState } from 'mastodon/components/empty_state';
import { FormStack, Combobox } from 'mastodon/components/form_fields';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
import ScrollableList from 'mastodon/components/scrollable_list';
import {
Article,
ItemList,
Scrollable,
} from 'mastodon/components/scrollable_list/components';
import { useSearchAccounts } from 'mastodon/features/lists/use_search_accounts';
import { useAccount } from 'mastodon/hooks/useAccount';
import { me } from 'mastodon/initial_state';
@@ -390,9 +394,8 @@ export const CollectionAccounts: React.FC<{
</Callout>
)}
<div className={classes.scrollableWrapper}>
<ScrollableList
scrollKey='collection-items'
<Scrollable className={classes.scrollableWrapper}>
<ItemList
className={classes.scrollableInner}
emptyMessage={
<EmptyState
@@ -413,18 +416,22 @@ export const CollectionAccounts: React.FC<{
}
/>
}
// TODO: Re-add `bindToDocument={!multiColumn}`
>
{accountIds.map((accountId) => (
<AddedAccountItem
{accountIds.map((accountId, index) => (
<Article
key={accountId}
accountId={accountId}
isRemovable={!isEditMode || !hasMinAccounts}
onRemove={handleRemoveAccountItem}
/>
aria-posinset={index}
aria-setsize={accountIds.length}
>
<AddedAccountItem
accountId={accountId}
isRemovable={!isEditMode || !hasMinAccounts}
onRemove={handleRemoveAccountItem}
/>
</Article>
))}
</ScrollableList>
</div>
</ItemList>
</Scrollable>
</FormStack>
{!isEditMode && (
<div className={classes.stickyFooter}>
@@ -49,12 +49,7 @@
flex-grow: 1;
}
.scrollableWrapper {
display: flex;
flex: 1;
margin-inline: -8px;
}
.scrollableWrapper,
.scrollableInner {
margin-inline: -8px;
}
@@ -11,7 +11,10 @@ import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list';
import {
ItemList,
Scrollable,
} from 'mastodon/components/scrollable_list/components';
import {
fetchAccountCollections,
selectAccountCollections,
@@ -85,16 +88,18 @@ export const Collections: React.FC<{
}
/>
<ScrollableList
scrollKey='collections'
emptyMessage={emptyMessage}
isLoading={status === 'loading'}
bindToDocument={!multiColumn}
>
{collections.map((item) => (
<CollectionListItem key={item.id} collection={item} />
))}
</ScrollableList>
<Scrollable>
<ItemList emptyMessage={emptyMessage} isLoading={status === 'loading'}>
{collections.map((item, index) => (
<CollectionListItem
key={item.id}
collection={item}
positionInList={index + 1}
listSize={collections.length}
/>
))}
</ItemList>
</Scrollable>
<Helmet>
<title>{intl.formatMessage(messages.heading)}</title>
@@ -322,6 +322,7 @@ class EmojiPickerDropdown extends PureComponent {
onPickEmoji: PropTypes.func.isRequired,
onSkinTone: PropTypes.func.isRequired,
skinTone: PropTypes.number.isRequired,
disabled: PropTypes.bool,
};
state = {
@@ -384,7 +385,7 @@ class EmojiPickerDropdown extends PureComponent {
};
render() {
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, disabled } = this.props;
const title = intl.formatMessage(messages.emoji);
const { active, loading, placement } = this.state;
@@ -396,6 +397,8 @@ class EmojiPickerDropdown extends PureComponent {
active={active}
iconComponent={MoodIcon}
onClick={this.onToggle}
disabled={disabled}
id="emoji"
inverted
/>
@@ -169,7 +169,9 @@ export function focusItemSibling(index: number, direction: 1 | -1) {
}
// Check if the sibling is a post or a 'follow suggestions' widget
let targetElement = siblingItem.querySelector<HTMLElement>('.focusable');
let targetElement = siblingItem.matches('.focusable')
? siblingItem
: siblingItem.querySelector<HTMLElement>('.focusable');
// Otherwise, check if the item is a 'load more' button.
if (!targetElement && siblingItem.matches('.load-more')) {