Use an emoji picker modal on mobile

fix-error-messages
Alex Gleason 2024-11-13 18:35:49 -06:00
rodzic 5380922d4e
commit ad24779343
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
7 zmienionych plików z 75 dodań i 35 usunięć

Wyświetl plik

@ -3,13 +3,16 @@ import dotsIcon from '@tabler/icons/outline/dots.svg';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { closeModal, openModal } from 'soapbox/actions/modals.ts';
import EmojiComponent from 'soapbox/components/ui/emoji.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import IconButton from 'soapbox/components/ui/icon-button.tsx';
import EmojiPickerDropdown from 'soapbox/features/emoji/components/emoji-picker-dropdown.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useClickOutside } from 'soapbox/hooks/useClickOutside.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
import { userTouching } from 'soapbox/is-mobile.ts';
import type { Emoji } from 'soapbox/features/emoji/index.ts';
@ -67,6 +70,7 @@ const EmojiSelector: React.FC<IEmojiSelector> = ({
const soapboxConfig = useSoapboxConfig();
const { customEmojiReacts } = useFeatures();
const dispatch = useAppDispatch();
const [expanded, setExpanded] = useState(false);
const { x, y, strategy, refs, update } = useFloating<HTMLElement>({
@ -75,7 +79,18 @@ const EmojiSelector: React.FC<IEmojiSelector> = ({
});
const handleExpand: React.MouseEventHandler = () => {
setExpanded(true);
if (userTouching.matches) {
dispatch(openModal('EMOJI_PICKER', {
onPickEmoji: (emoji: Emoji) => {
handlePickEmoji(emoji);
dispatch(closeModal('EMOJI_PICKER'));
},
}));
onClose?.();
} else {
setExpanded(true);
}
};
const handlePickEmoji = (emoji: Emoji) => {
@ -95,9 +110,7 @@ const EmojiSelector: React.FC<IEmojiSelector> = ({
}, [visible]);
useClickOutside(refs, () => {
if (onClose) {
onClose();
}
onClose?.();
});
return (

Wyświetl plik

@ -16,7 +16,7 @@ const messages = defineMessages({
});
const themes = {
normal: 'bg-white p-6 shadow-xl',
normal: 'bg-white black:bg-black dark:bg-primary-900 p-6 shadow-xl text-gray-900 dark:text-gray-100',
transparent: 'bg-transparent p-0 shadow-none',
};
@ -105,7 +105,7 @@ const Modal = forwardRef<HTMLDivElement, IModal>(({
<div
ref={ref}
data-testid='modal'
className={clsx(className, 'pointer-events-auto mx-auto block w-full rounded-2xl text-start align-middle text-gray-900 transition-all black:bg-black dark:bg-primary-900 dark:text-gray-100', widths[width], themes[theme])}
className={clsx(className, 'pointer-events-auto mx-auto block w-full rounded-2xl text-start align-middle transition-all', widths[width], themes[theme])}
>
<div className='w-full justify-between sm:flex sm:items-start'>
<div className='w-full'>

Wyświetl plik

@ -1,5 +1,5 @@
import { Map as ImmutableMap } from 'immutable';
import { useEffect, useState, useLayoutEffect, Suspense } from 'react';
import React, { useEffect, useState, useLayoutEffect, Suspense } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { createSelector } from 'reselect';
@ -45,9 +45,9 @@ export interface IEmojiPickerDropdown {
onPickEmoji?: (emoji: Emoji) => void;
condensed?: boolean;
withCustom?: boolean;
visible: boolean;
setVisible: (value: boolean) => void;
update: (() => any) | null;
visible?: boolean;
setVisible?: (value: boolean) => void;
update?: (() => any) | null;
}
const perLine = 8;
@ -105,8 +105,13 @@ const getCustomEmojis = createSelector([
}
}));
interface IRenderAfter {
children: React.ReactNode;
update: () => void;
}
// Fixes render bug where popover has a delayed position update
const RenderAfter = ({ children, update }: any) => {
const RenderAfter: React.FC<IRenderAfter> = ({ children, update }) => {
const [nextTick, setNextTick] = useState(false);
useEffect(() => {
@ -125,7 +130,7 @@ const RenderAfter = ({ children, update }: any) => {
};
const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
onPickEmoji, visible, setVisible, update, withCustom = true,
onPickEmoji, visible = true, setVisible, update, withCustom = true,
}) => {
const intl = useIntl();
const dispatch = useAppDispatch();
@ -136,7 +141,7 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
const frequentlyUsedEmojis = useAppSelector((state) => getFrequentlyUsedEmojis(state));
const handlePick = (emoji: any) => {
setVisible(false);
setVisible?.(false);
let pickedEmoji: Emoji;
@ -213,28 +218,30 @@ const EmojiPickerDropdown: React.FC<IEmojiPickerDropdown> = ({
document.body.style.overflow = '';
}, []);
if (!visible) {
return null;
}
return (
visible ? (
<Suspense>
<RenderAfter update={update}>
<EmojiPicker
custom={withCustom ? [{ emojis: buildCustomEmojis(customEmojis) }] : undefined}
title={title}
onEmojiSelect={handlePick}
recent={frequentlyUsedEmojis}
perLine={8}
skin={handleSkinTone}
emojiSize={22}
emojiButtonSize={34}
set='twitter'
theme={theme}
i18n={getI18n()}
skinTonePosition='search'
previewPosition='none'
/>
</RenderAfter>
</Suspense>
) : null
<Suspense>
<RenderAfter update={update ?? (() => {})}>
<EmojiPicker
custom={withCustom ? [{ emojis: buildCustomEmojis(customEmojis) }] : undefined}
title={title}
onEmojiSelect={handlePick}
recent={frequentlyUsedEmojis}
perLine={8}
skin={handleSkinTone}
emojiSize={22}
emojiButtonSize={34}
set='twitter'
theme={theme}
i18n={getI18n()}
skinTonePosition='search'
previewPosition='none'
/>
</RenderAfter>
</Suspense>
);
};

Wyświetl plik

@ -19,7 +19,7 @@ const Picker: React.FC<any> = (props) => {
new EmojiPicker(input);
}, []);
return <div ref={ref} />;
return <div className='flex justify-center' ref={ref} />;
};
export default Picker;

Wyświetl plik

@ -18,6 +18,7 @@ import {
EditDomainModal,
EditFederationModal,
EmbedModal,
EmojiPickerModal,
EventMapModal,
EventParticipantsModal,
FamiliarFollowersModal,
@ -72,6 +73,7 @@ const MODAL_COMPONENTS: Record<string, React.ExoticComponent<any>> = {
'EDIT_FEDERATION': EditFederationModal,
'EDIT_RULE': EditRuleModal,
'EMBED': EmbedModal,
'EMOJI_PICKER': EmojiPickerModal,
'EVENT_MAP': EventMapModal,
'EVENT_PARTICIPANTS': EventParticipantsModal,
'FAMILIAR_FOLLOWERS': FamiliarFollowersModal,

Wyświetl plik

@ -0,0 +1,17 @@
import Modal from 'soapbox/components/ui/modal.tsx';
import EmojiPickerDropdown from 'soapbox/features/emoji/components/emoji-picker-dropdown.tsx';
import { Emoji } from 'soapbox/features/emoji/index.ts';
interface IEmojiPickerModal {
onPickEmoji?: (emoji: Emoji) => void;
}
export const EmojiPickerModal: React.FC<IEmojiPickerModal> = (props) => {
return (
<Modal className='flex' theme='transparent'>
<EmojiPickerDropdown {...props} />
</Modal>
);
};
export default EmojiPickerModal;

Wyświetl plik

@ -2,6 +2,7 @@ import { lazy } from 'react';
export const AboutPage = lazy(() => import('soapbox/features/about/index.tsx'));
export const EmojiPicker = lazy(() => import('soapbox/features/emoji/components/emoji-picker.tsx'));
export const EmojiPickerModal = lazy(() => import('soapbox/features/ui/components/modals/emoji-picker-modal.tsx'));
export const Notifications = lazy(() => import('soapbox/features/notifications/index.tsx'));
export const LandingTimeline = lazy(() => import('soapbox/features/landing-timeline/index.tsx'));
export const HomeTimeline = lazy(() => import('soapbox/features/home-timeline/index.tsx'));