Merge remote-tracking branch 'soapbox/develop' into mastodon-groups
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
|
||||
import { __stub } from 'soapbox/api';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import { render, screen, waitFor } from '../../../../jest/test-helpers';
|
||||
import Search from '../search';
|
||||
|
||||
describe('<Search />', () => {
|
||||
@@ -22,7 +22,9 @@ describe('<Search />', () => {
|
||||
|
||||
await user.type(screen.getByLabelText('Search'), '@jus');
|
||||
|
||||
expect(screen.getByLabelText('Search')).toHaveValue('@jus');
|
||||
expect(screen.getByTestId('account')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText('Search')).toHaveValue('@jus');
|
||||
expect(screen.getByTestId('account')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'soapbox/actions/compose';
|
||||
import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest-input';
|
||||
import AutosuggestTextarea from 'soapbox/components/autosuggest-textarea';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { Button, HStack, Stack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures, useInstance, usePrevious } from 'soapbox/hooks';
|
||||
import { isMobile } from 'soapbox/is-mobile';
|
||||
@@ -76,7 +75,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
const showSearch = useAppSelector((state) => state.search.submitted && !state.search.hidden);
|
||||
const isModalOpen = useAppSelector((state) => !!(state.modals.size && state.modals.last()!.modalType === 'COMPOSE'));
|
||||
const maxTootChars = configuration.getIn(['statuses', 'max_characters']) as number;
|
||||
const scheduledStatusCount = useAppSelector((state) => state.get('scheduled_statuses').size);
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const features = useFeatures();
|
||||
|
||||
const { text, suggestions, spoiler, spoiler_text: spoilerText, privacy, focusDate, caretPosition, is_submitting: isSubmitting, is_changing_upload: isChangingUpload, is_uploading: isUploading, schedule: scheduledAt, group_id: groupId } = compose;
|
||||
@@ -242,25 +241,18 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
const disabledButton = disabled || isUploading || isChangingUpload || length(countedText) > maxTootChars || (countedText.length !== 0 && countedText.trim().length === 0 && !anyMedia);
|
||||
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth);
|
||||
|
||||
let publishText: string | JSX.Element = '';
|
||||
let publishText: string = '';
|
||||
let publishIcon: string | undefined;
|
||||
let textareaPlaceholder: MessageDescriptor;
|
||||
|
||||
if (isEditing) {
|
||||
publishText = intl.formatMessage(messages.saveChanges);
|
||||
} else if (privacy === 'direct') {
|
||||
publishText = (
|
||||
<>
|
||||
<Icon src={require('@tabler/icons/mail.svg')} />
|
||||
{intl.formatMessage(messages.message)}
|
||||
</>
|
||||
);
|
||||
publishText = intl.formatMessage(messages.message);
|
||||
publishIcon = require('@tabler/icons/mail.svg');
|
||||
} else if (privacy === 'private') {
|
||||
publishText = (
|
||||
<>
|
||||
<Icon src={require('@tabler/icons/lock.svg')} />
|
||||
{intl.formatMessage(messages.publish)}
|
||||
</>
|
||||
);
|
||||
publishText = intl.formatMessage(messages.publish);
|
||||
publishIcon = require('@tabler/icons/lock.svg');
|
||||
} else {
|
||||
publishText = privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||
}
|
||||
@@ -356,7 +348,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<Button type='submit' theme='primary' text={publishText} disabled={disabledButton} />
|
||||
<Button type='submit' theme='primary' text={publishText} icon={publishIcon} disabled={disabledButton} />
|
||||
</HStack>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
@@ -72,8 +72,8 @@ const EmojiPickerMenu: React.FC<IEmojiPickerMenu> = ({
|
||||
|
||||
categoriesSort.splice(1, 0, ...Array.from(categoriesFromEmojis(customEmojis) as Set<string>).sort());
|
||||
|
||||
const handleDocumentClick = useCallback(e => {
|
||||
if (node.current && !node.current.contains(e.target)) {
|
||||
const handleDocumentClick = useCallback((e: MouseEvent | TouchEvent) => {
|
||||
if (node.current && !node.current.contains(e.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -19,8 +19,8 @@ const ModifierPickerMenu: React.FC<IModifierPickerMenu> = ({ active, onSelect, o
|
||||
onSelect(+e.currentTarget.getAttribute('data-index')! * 1);
|
||||
};
|
||||
|
||||
const handleDocumentClick = useCallback((e => {
|
||||
if (node.current && !node.current.contains(e.target)) {
|
||||
const handleDocumentClick = useCallback(((e: MouseEvent | TouchEvent) => {
|
||||
if (node.current && !node.current.contains(e.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
}), []);
|
||||
|
||||
@@ -40,6 +40,7 @@ const ReplyIndicator: React.FC<IReplyIndicator> = ({ status, hideActions, onCanc
|
||||
timestamp={status.created_at}
|
||||
showProfileHoverCard={false}
|
||||
withLinkToProfile={false}
|
||||
hideActions={hideActions}
|
||||
/>
|
||||
|
||||
<Text
|
||||
|
||||
@@ -15,7 +15,7 @@ import PlaceholderAccount from 'soapbox/features/placeholder/components/placehol
|
||||
import PlaceholderGroupCard from 'soapbox/features/placeholder/components/placeholder-group-card';
|
||||
import PlaceholderHashtag from 'soapbox/features/placeholder/components/placeholder-hashtag';
|
||||
import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder-status';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
|
||||
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import type { VirtuosoHandle } from 'react-virtuoso';
|
||||
@@ -33,6 +33,7 @@ const SearchResults = () => {
|
||||
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
const value = useAppSelector((state) => state.search.submittedValue);
|
||||
const results = useAppSelector((state) => state.search.results);
|
||||
@@ -51,7 +52,8 @@ const SearchResults = () => {
|
||||
const selectFilter = (newActiveFilter: SearchFilter) => dispatch(setFilter(newActiveFilter));
|
||||
|
||||
const renderFilterBar = () => {
|
||||
const items = [
|
||||
const items = [];
|
||||
items.push(
|
||||
{
|
||||
text: intl.formatMessage(messages.accounts),
|
||||
action: () => selectFilter('accounts'),
|
||||
@@ -62,17 +64,23 @@ const SearchResults = () => {
|
||||
action: () => selectFilter('statuses'),
|
||||
name: 'statuses',
|
||||
},
|
||||
);
|
||||
|
||||
if (features.groups) items.push(
|
||||
{
|
||||
text: intl.formatMessage(messages.groups),
|
||||
action: () => selectFilter('groups'),
|
||||
name: 'groups',
|
||||
},
|
||||
);
|
||||
|
||||
items.push(
|
||||
{
|
||||
text: intl.formatMessage(messages.hashtags),
|
||||
action: () => selectFilter('hashtags'),
|
||||
name: 'hashtags',
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
return <Tabs items={items} activeItem={selectedFilter} />;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import classNames from 'clsx';
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
@@ -17,7 +15,8 @@ import {
|
||||
import AutosuggestAccountInput from 'soapbox/components/autosuggest-account-input';
|
||||
import { Input } from 'soapbox/components/ui';
|
||||
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { AppDispatch, RootState } from 'soapbox/store';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
||||
@@ -25,7 +24,7 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
function redirectToAccount(accountId: string, routerHistory: any) {
|
||||
return (_dispatch: any, getState: () => ImmutableMap<string, any>) => {
|
||||
return (_dispatch: AppDispatch, getState: () => RootState) => {
|
||||
const acct = getState().getIn(['accounts', accountId, 'acct']);
|
||||
|
||||
if (acct && routerHistory) {
|
||||
@@ -49,7 +48,7 @@ const Search = (props: ISearch) => {
|
||||
openInRoute = false,
|
||||
} = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useAppDispatch();
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -127,6 +126,7 @@ const Search = (props: ISearch) => {
|
||||
onFocus: handleFocus,
|
||||
autoFocus: autoFocus,
|
||||
theme: 'search',
|
||||
className: 'pr-10 rtl:pl-10 rtl:pr-3',
|
||||
};
|
||||
|
||||
if (autosuggest) {
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface IUploadButton {
|
||||
resetFileKey: number | null,
|
||||
className?: string,
|
||||
iconClassName?: string,
|
||||
icon?: string,
|
||||
}
|
||||
|
||||
const UploadButton: React.FC<IUploadButton> = ({
|
||||
@@ -31,6 +32,7 @@ const UploadButton: React.FC<IUploadButton> = ({
|
||||
resetFileKey,
|
||||
className = 'text-gray-600 hover:text-gray-700 dark:hover:text-gray-500',
|
||||
iconClassName,
|
||||
icon,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const { configuration } = useInstance();
|
||||
@@ -52,9 +54,11 @@ const UploadButton: React.FC<IUploadButton> = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const src = onlyImages(attachmentTypes)
|
||||
? require('@tabler/icons/photo.svg')
|
||||
: require('@tabler/icons/paperclip.svg');
|
||||
const src = icon || (
|
||||
onlyImages(attachmentTypes)
|
||||
? require('@tabler/icons/photo.svg')
|
||||
: require('@tabler/icons/paperclip.svg')
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { length } from 'stringz';
|
||||
import ProgressCircle from 'soapbox/components/progress-circle';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'compose.character_counter.title', defaultMessage: 'Used {chars} out of {maxChars} characters' },
|
||||
title: { id: 'compose.character_counter.title', defaultMessage: 'Used {chars} out of {maxChars} {maxChars, plural, one {character} other {characters}}' },
|
||||
});
|
||||
|
||||
interface IVisualCharacterCounter {
|
||||
|
||||
Reference in New Issue
Block a user