Merge remote-tracking branch 'origin/develop' into renovate/major-react-monorepo

This commit is contained in:
Alex Gleason 2023-01-07 14:34:06 -06:00
commit 7ac41b1bf1
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
15 changed files with 65 additions and 39 deletions

View file

@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Datepicker: correctly default to the current year. - Datepicker: correctly default to the current year.
- Scheduled posts: fix page crashing on deleting a scheduled post. - Scheduled posts: fix page crashing on deleting a scheduled post.
- Events: don't crash when searching for a location. - Events: don't crash when searching for a location.
- Search: fixes an abort error when using the navbar search component.
- Posts: fix monospace font in Markdown code blocks.
- Modals: fix action buttons overflow
- Editing: don't insert edited posts to the top of the feed.
## [3.0.0] - 2022-12-25 ## [3.0.0] - 2022-12-25

View file

@ -6,7 +6,7 @@ import api from '../api';
import { loadCredentials } from './auth'; import { loadCredentials } from './auth';
import { importFetchedAccount } from './importer'; import { importFetchedAccount } from './importer';
import type { AxiosError, AxiosRequestHeaders } from 'axios'; import type { AxiosError, RawAxiosRequestHeaders } from 'axios';
import type { AppDispatch, RootState } from 'soapbox/store'; import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity } from 'soapbox/types/entities'; import type { APIEntity } from 'soapbox/types/entities';
@ -66,7 +66,7 @@ const patchMe = (params: Record<string, any>, isFormData = false) =>
(dispatch: AppDispatch, getState: () => RootState) => { (dispatch: AppDispatch, getState: () => RootState) => {
dispatch(patchMeRequest()); dispatch(patchMeRequest());
const headers: AxiosRequestHeaders = isFormData ? { const headers: RawAxiosRequestHeaders = isFormData ? {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
} : {}; } : {};

View file

@ -68,7 +68,7 @@ const createStatus = (params: Record<string, any>, idempotencyKey: string, statu
} }
dispatch(importFetchedStatus(status, idempotencyKey)); dispatch(importFetchedStatus(status, idempotencyKey));
dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey }); dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId });
// Poll the backend for the updated card // Poll the backend for the updated card
if (status.expectsCard) { if (status.expectsCard) {

View file

@ -44,7 +44,7 @@ const AutosuggestAccountInput: React.FC<IAutosuggestAccountInput> = ({
setAccountIds(ImmutableOrderedSet()); setAccountIds(ImmutableOrderedSet());
}; };
const handleAccountSearch = useCallback(throttle(q => { const handleAccountSearch = useCallback(throttle((q) => {
const params = { q, limit, resolve: false }; const params = { q, limit, resolve: false };
dispatch(accountSearch(params, controller.current.signal)) dispatch(accountSearch(params, controller.current.signal))
@ -53,7 +53,7 @@ const AutosuggestAccountInput: React.FC<IAutosuggestAccountInput> = ({
setAccountIds(ImmutableOrderedSet(accountIds)); setAccountIds(ImmutableOrderedSet(accountIds));
}) })
.catch(noOp); .catch(noOp);
}, 900, { leading: false, trailing: true }), [limit]); }, 900, { leading: true, trailing: true }), [limit]);
const handleChange: React.ChangeEventHandler<HTMLInputElement> = e => { const handleChange: React.ChangeEventHandler<HTMLInputElement> = e => {
refreshCancelToken(); refreshCancelToken();

View file

@ -98,13 +98,21 @@ const FeedCarousel = () => {
const [pinnedAvatar, setPinnedAvatar] = useState<Avatar | null>(null); const [pinnedAvatar, setPinnedAvatar] = useState<Avatar | null>(null);
const avatarsToList = useMemo(() => { const avatarsToList = useMemo(() => {
const list = avatars.filter((avatar) => avatar.account_id !== pinnedAvatar?.account_id); let list: (Avatar | null)[] = avatars.filter((avatar) => avatar.account_id !== pinnedAvatar?.account_id);
// If we have an Avatar pinned, let's create a new array with "null"
// in the first position of each page.
if (pinnedAvatar) { if (pinnedAvatar) {
return [null, ...list]; const index = (currentPage - 1) * pageSize;
list = [
...list.slice(0, index),
null,
...list.slice(index),
];
} }
return list; return list;
}, [avatars, pinnedAvatar]); }, [avatars, pinnedAvatar, currentPage, pageSize]);
const numberOfPages = Math.ceil(avatars.length / pageSize); const numberOfPages = Math.ceil(avatars.length / pageSize);
const widthPerAvatar = width / (Math.floor(width / 80)); const widthPerAvatar = width / (Math.floor(width / 80));

View file

@ -28,6 +28,7 @@ const BirthdaysModal = ({ onClose }: IBirthdaysModal) => {
<ScrollableList <ScrollableList
scrollKey='birthdays' scrollKey='birthdays'
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
className='max-w-full'
itemClassName='pb-3' itemClassName='pb-3'
> >
{accountIds.map(id => {accountIds.map(id =>

View file

@ -2,7 +2,8 @@ import React, { useEffect } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { fetchEventParticipations } from 'soapbox/actions/events'; import { fetchEventParticipations } from 'soapbox/actions/events';
import { Modal, Spinner, Stack } from 'soapbox/components/ui'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Modal, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container'; import AccountContainer from 'soapbox/containers/account-container';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
@ -33,16 +34,19 @@ const EventParticipantsModal: React.FC<IEventParticipantsModal> = ({ onClose, st
if (!accountIds) { if (!accountIds) {
body = <Spinner />; body = <Spinner />;
} else { } else {
const emptyMessage = <FormattedMessage id='empty_column.event_participants' defaultMessage='No one joined this event yet. When someone does, they will show up here.' />;
body = ( body = (
<Stack space={3}> <ScrollableList
{accountIds.size > 0 ? ( scrollKey='event_participations'
accountIds.map((id) => emptyMessage={emptyMessage}
className='max-w-full'
itemClassName='pb-3'
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />, <AccountContainer key={id} id={id} />,
)
) : (
<FormattedMessage id='empty_column.event_participants' defaultMessage='No one joined this event yet. When someone does, they will show up here.' />
)} )}
</Stack> </ScrollableList>
); );
} }

View file

@ -2,7 +2,8 @@ import React, { useEffect } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { fetchFavourites } from 'soapbox/actions/interactions'; import { fetchFavourites } from 'soapbox/actions/interactions';
import { Modal, Spinner, Stack } from 'soapbox/components/ui'; import ScrollableList from 'soapbox/components/scrollable-list';
import { Modal, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container'; import AccountContainer from 'soapbox/containers/account-container';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
@ -33,16 +34,19 @@ const FavouritesModal: React.FC<IFavouritesModal> = ({ onClose, statusId }) => {
if (!accountIds) { if (!accountIds) {
body = <Spinner />; body = <Spinner />;
} else { } else {
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has liked this post yet. When someone does, they will show up here.' />;
body = ( body = (
<Stack space={3}> <ScrollableList
{accountIds.size > 0 ? ( scrollKey='favourites'
accountIds.map((id) => emptyMessage={emptyMessage}
className='max-w-full'
itemClassName='pb-3'
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />, <AccountContainer key={id} id={id} />,
)
) : (
<FormattedMessage id='empty_column.favourites' defaultMessage='No one has liked this post yet. When someone does, they will show up here.' />
)} )}
</Stack> </ScrollableList>
); );
} }

View file

@ -41,6 +41,7 @@ const MentionsModal: React.FC<IMentionsModal> = ({ onClose, statusId }) => {
body = ( body = (
<ScrollableList <ScrollableList
scrollKey='mentions' scrollKey='mentions'
className='max-w-full'
itemClassName='pb-3' itemClassName='pb-3'
> >
{accountIds.map(id => {accountIds.map(id =>

View file

@ -1,3 +1,4 @@
import classNames from 'clsx';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
@ -84,7 +85,9 @@ const ReactionsModal: React.FC<IReactionsModal> = ({ onClose, statusId, reaction
<ScrollableList <ScrollableList
scrollKey='reactions' scrollKey='reactions'
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
className='mt-4' className={classNames('max-w-full', {
'mt-4': reactions.size > 0,
})}
itemClassName='pb-3' itemClassName='pb-3'
> >
{accounts.map((account) => {accounts.map((account) =>

View file

@ -41,6 +41,7 @@ const ReblogsModal: React.FC<IReblogsModal> = ({ onClose, statusId }) => {
<ScrollableList <ScrollableList
scrollKey='reblogs' scrollKey='reblogs'
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
className='max-w-full'
itemClassName='pb-3' itemClassName='pb-3'
> >
{accountIds.map((id) => {accountIds.map((id) =>

View file

@ -314,7 +314,7 @@ export default function timelines(state: State = initialState, action: AnyAction
if (action.params.scheduled_at) return state; if (action.params.scheduled_at) return state;
return importPendingStatus(state, action.params, action.idempotencyKey); return importPendingStatus(state, action.params, action.idempotencyKey);
case STATUS_CREATE_SUCCESS: case STATUS_CREATE_SUCCESS:
if (action.status.scheduled_at) return state; if (action.status.scheduled_at || action.editing) return state;
return importStatus(state, action.status, action.idempotencyKey); return importStatus(state, action.status, action.idempotencyKey);
case TIMELINE_EXPAND_REQUEST: case TIMELINE_EXPAND_REQUEST:
return setLoading(state, action.timeline, true); return setLoading(state, action.timeline, true);

View file

@ -7,6 +7,7 @@
@import '~@fontsource/inter/600.css'; @import '~@fontsource/inter/600.css';
@import '~@fontsource/inter/700.css'; @import '~@fontsource/inter/700.css';
@import '~@fontsource/inter/900.css'; @import '~@fontsource/inter/900.css';
@import '~@fontsource/roboto-mono/400.css';
@import 'mixins'; @import 'mixins';
@import 'themes'; @import 'themes';
@ -16,7 +17,6 @@
@import 'accounts'; @import 'accounts';
@import 'loading'; @import 'loading';
@import 'ui'; @import 'ui';
// @import 'introduction';
@import 'emoji-picker'; @import 'emoji-picker';
@import 'rtl'; @import 'rtl';
@import 'accessibility'; @import 'accessibility';

View file

@ -50,7 +50,7 @@
"@babel/preset-typescript": "^7.17.12", "@babel/preset-typescript": "^7.17.12",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@fontsource/inter": "^4.5.1", "@fontsource/inter": "^4.5.1",
"@fontsource/roboto": "^4.5.0", "@fontsource/roboto-mono": "^4.5.8",
"@gamestdio/websocket": "^0.3.2", "@gamestdio/websocket": "^0.3.2",
"@jest/globals": "^29.0.0", "@jest/globals": "^29.0.0",
"@lcdp/offline-plugin": "^5.1.0", "@lcdp/offline-plugin": "^5.1.0",
@ -97,7 +97,7 @@
"@types/webpack-deadcode-plugin": "^0.1.2", "@types/webpack-deadcode-plugin": "^0.1.2",
"array-includes": "^3.1.5", "array-includes": "^3.1.5",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"axios": "^1.0.0-alpha.1", "axios": "^1.2.2",
"axios-mock-adapter": "^1.21.1", "axios-mock-adapter": "^1.21.1",
"babel-loader": "^8.2.5", "babel-loader": "^8.2.5",
"babel-plugin-lodash": "^3.3.4", "babel-plugin-lodash": "^3.3.4",

View file

@ -1404,10 +1404,10 @@
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.5.1.tgz#058d8a02354f3c78e369d452c15d33557ec1b705" resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.5.1.tgz#058d8a02354f3c78e369d452c15d33557ec1b705"
integrity sha512-mvtOvXNNVLlF1p/UbLgLrmz2RCOl6Ow+TqyiK10SosoLKX7edsXYiHFHb7XIZdjII6F2sJVPPsJXWhBnbXT2DQ== integrity sha512-mvtOvXNNVLlF1p/UbLgLrmz2RCOl6Ow+TqyiK10SosoLKX7edsXYiHFHb7XIZdjII6F2sJVPPsJXWhBnbXT2DQ==
"@fontsource/roboto@^4.5.0": "@fontsource/roboto-mono@^4.5.8":
version "4.5.0" version "4.5.8"
resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.5.0.tgz#d6f925668ba6af46707f1040c43aff498ba204bb" resolved "https://registry.yarnpkg.com/@fontsource/roboto-mono/-/roboto-mono-4.5.8.tgz#f3a48195528b10e154c98365671de14e27605bdb"
integrity sha512-ja4XYw/9kNRFM5Ndk9vwzHWsdBMXczyBazFkTXJQ74QQBnT0BbSsHn0pF60AU0Iznig1Wt9x3rADfG8LANvMpw== integrity sha512-AW44UkbQD0w1CT5mzDbsvhGZ6/bb0YmZzoELj6Sx8vcVEzcbYGUdt2Dtl5zqlOuYMWQFY1mniwWyVv+Bm/lVxw==
"@formatjs/ecma402-abstract@1.11.4": "@formatjs/ecma402-abstract@1.11.4":
version "1.11.4" version "1.11.4"
@ -3658,10 +3658,10 @@ axios-mock-adapter@^1.21.1:
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
is-buffer "^2.0.5" is-buffer "^2.0.5"
axios@^1.0.0-alpha.1: axios@^1.2.2:
version "1.0.0-alpha.1" version "1.2.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.0.0-alpha.1.tgz#ce69c17ca7605d01787ca754dd906e6fccdf71ee" resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.2.tgz#72681724c6e6a43a9fea860fc558127dbe32f9f1"
integrity sha512-p+meG161943WT+K7sJYquHR46xxi/z0tk7vnSmEf/LrfEAyiP+0uTMMYk1OEo1IRF18oGRhnFxN1y8fLcXaTMw== integrity sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==
dependencies: dependencies:
follow-redirects "^1.15.0" follow-redirects "^1.15.0"
form-data "^4.0.0" form-data "^4.0.0"