mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-13 02:36:36 -05:00
Compose side fixes (#40000)
This commit is contained in:
@@ -24,6 +24,7 @@ export interface ApiImageAttachmentJSON extends BaseApiMediaAttachmentJSON {
|
||||
meta: {
|
||||
original: ApiImageAttachmentMetaJSON;
|
||||
small: ApiImageAttachmentMetaJSON;
|
||||
focus?: ApiFocusAttachmentMetaJSON;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,10 +43,7 @@ export interface ApiVideoAttachmentJSON extends BaseApiMediaAttachmentJSON {
|
||||
colors: ApiColorsAttachmentMetaJSON;
|
||||
original: ApiVideoAttachmentMetaJSON;
|
||||
small: ApiImageAttachmentMetaJSON;
|
||||
focus: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
focus?: ApiFocusAttachmentMetaJSON;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,6 +52,7 @@ export interface ApiGifvAttachmentJSON extends BaseApiMediaAttachmentJSON {
|
||||
meta: {
|
||||
original: ApiVideoAttachmentMetaJSON;
|
||||
small: ApiImageAttachmentMetaJSON;
|
||||
focus?: ApiFocusAttachmentMetaJSON;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,3 +88,8 @@ export interface ApiColorsAttachmentMetaJSON {
|
||||
foreground: string;
|
||||
accent: string;
|
||||
}
|
||||
|
||||
export interface ApiFocusAttachmentMetaJSON {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ export const textAtCursorMatchesToken = (
|
||||
word = word.trim();
|
||||
|
||||
if (word.length < 3 || (word[0] && !searchTokens.includes(word[0]))) {
|
||||
return [null, null];
|
||||
return [null, null] as const;
|
||||
}
|
||||
|
||||
if (word.length > 0) {
|
||||
return [left + 1, word];
|
||||
return [left + 1, word] as const;
|
||||
} else {
|
||||
return [null, null];
|
||||
return [null, null] as const;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ const AutosuggestTextarea = forwardRef(({
|
||||
}
|
||||
}, [lang]);
|
||||
|
||||
const renderSuggestion = (suggestion, i) => {
|
||||
const renderSuggestion = useCallback((suggestion, i) => {
|
||||
let inner, key;
|
||||
|
||||
if (suggestion.type === 'emoji') {
|
||||
@@ -172,7 +172,7 @@ const AutosuggestTextarea = forwardRef(({
|
||||
{inner}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}, [selectedSuggestion, handleSuggestionClick]);
|
||||
|
||||
const handleRef = useCallback((element) => {
|
||||
textareaRef.current = element;
|
||||
|
||||
@@ -95,13 +95,18 @@ export interface PopoverProps {
|
||||
* enable it to be sized and positioned. The `ref` prop
|
||||
* is not passed when the `popoverElement` prop is provided.
|
||||
*/
|
||||
props: Record<string, unknown> & {
|
||||
ref?: React.RefCallback<HTMLElement>;
|
||||
style: React.CSSProperties;
|
||||
};
|
||||
props: PopoverChildProps;
|
||||
}) => React.ReactNode;
|
||||
}
|
||||
|
||||
export interface PopoverChildProps {
|
||||
ref?: React.RefCallback<HTMLElement>;
|
||||
style: React.CSSProperties;
|
||||
'data-popover-placement': Placement;
|
||||
'data-popover-reference-hidden'?: boolean;
|
||||
'data-popover-escaped'?: boolean;
|
||||
}
|
||||
|
||||
export const Popover: React.FC<PopoverProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
|
||||
@@ -56,13 +56,15 @@ const getFrequentlyUsedLanguages = createSelector(
|
||||
|
||||
const isTextLongEnoughForGuess = (text: string) => text.length > 20;
|
||||
|
||||
const LanguageDropdownMenu: React.FC<{
|
||||
const emptyArray: Language[] = [];
|
||||
|
||||
export const LanguageDropdownMenu: React.FC<{
|
||||
value: string;
|
||||
guess?: string;
|
||||
guess: string;
|
||||
onClose: () => void;
|
||||
onChange: (arg0: string) => void;
|
||||
}> = ({ value, guess, onClose, onChange }) => {
|
||||
const languages = preloadedLanguages as Language[];
|
||||
const languages = preloadedLanguages ?? emptyArray;
|
||||
const intl = useIntl();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
@@ -317,7 +319,7 @@ export const LanguageDropdown: React.FC = () => {
|
||||
const text = useAppSelector((state) => state.compose.get('text') as string);
|
||||
|
||||
const current =
|
||||
(preloadedLanguages as Language[]).find((lang) => lang[0] === value) ?? [];
|
||||
(preloadedLanguages ?? []).find((lang) => lang[0] === value) ?? [];
|
||||
|
||||
const handleMouseDown = useCallback(() => {
|
||||
if (!open && document.activeElement instanceof HTMLElement) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
accept_content_types: [],
|
||||
accept_content_types: ImmutableList(),
|
||||
});
|
||||
|
||||
export default function meta(state = initialState, action) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const selectPlainStatus = createAppSelector(
|
||||
export const selectAccountStatus = createAppSelector(
|
||||
[
|
||||
selectPlainStatus,
|
||||
(state, statusId: string) => {
|
||||
(state, statusId?: string | null) => {
|
||||
const accountId = state.statuses.getIn([statusId, 'account']);
|
||||
if (typeof accountId !== 'string') {
|
||||
return null;
|
||||
|
||||
@@ -2,9 +2,16 @@ import type { CompactEmoji } from 'emojibase';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { action } from 'storybook/actions';
|
||||
|
||||
import type { MediaAttachmentType } from '@/mastodon/api_types/media_attachments';
|
||||
import { toSupportedLocale } from '@/mastodon/features/emoji/locale';
|
||||
|
||||
import { customEmojiFactory, relationshipsFactoryAPI } from './factories';
|
||||
import {
|
||||
customEmojiFactory,
|
||||
mediaAttachmentFactoryAPI,
|
||||
relationshipsFactoryAPI,
|
||||
} from './factories';
|
||||
|
||||
const mediaStorageMap = new Map<string, File>();
|
||||
|
||||
export const mockHandlers = {
|
||||
mute: http.post<{ id: string }>('/api/v1/accounts/:id/mute', ({ params }) => {
|
||||
@@ -43,6 +50,53 @@ export const mockHandlers = {
|
||||
);
|
||||
},
|
||||
),
|
||||
mediaUpload: http.post('/api/v2/media', async ({ request }) => {
|
||||
action('uploaded media')();
|
||||
|
||||
const formData = await request.formData();
|
||||
const file = formData.get('file');
|
||||
if (!file) {
|
||||
return new HttpResponse('Missing media', { status: 400 });
|
||||
}
|
||||
|
||||
if (!(file instanceof File)) {
|
||||
return new HttpResponse('Media is not file', { status: 400 });
|
||||
}
|
||||
|
||||
const { type: mimeType } = file;
|
||||
const id = mediaStorageMap.size.toString();
|
||||
mediaStorageMap.set(id, file);
|
||||
let type: MediaAttachmentType = 'unknown';
|
||||
if (mimeType === 'image/gif') {
|
||||
type = 'gifv';
|
||||
} else if (mimeType.startsWith('image/')) {
|
||||
type = 'image';
|
||||
} else if (mimeType.startsWith('video/')) {
|
||||
type = 'video';
|
||||
} else if (mimeType.startsWith('audio/')) {
|
||||
type = 'audio';
|
||||
}
|
||||
|
||||
return HttpResponse.json(
|
||||
mediaAttachmentFactoryAPI({
|
||||
id,
|
||||
type,
|
||||
url: `/mock_media/${id}`,
|
||||
preview_url: `/mock_media/${id}`,
|
||||
}),
|
||||
);
|
||||
}),
|
||||
mediaGet: http.get<{ id: string }>('/mock_media/:id', async ({ params }) => {
|
||||
const { id } = params;
|
||||
action(`getting media id ${id}`)();
|
||||
|
||||
const media = mediaStorageMap.get(id);
|
||||
if (!media) {
|
||||
return new HttpResponse('Not found', { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.arrayBuffer(await media.arrayBuffer());
|
||||
}),
|
||||
emojiCustomData: http.get('/api/v1/custom_emojis', () => {
|
||||
action('fetching custom emoji data')();
|
||||
return HttpResponse.json([customEmojiFactory()]);
|
||||
|
||||
Reference in New Issue
Block a user