diff --git a/src/components/ui/emoji-selector.tsx b/src/components/ui/emoji-selector.tsx index 77d0cea4c..32303800f 100644 --- a/src/components/ui/emoji-selector.tsx +++ b/src/components/ui/emoji-selector.tsx @@ -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 = ({ const soapboxConfig = useSoapboxConfig(); const { customEmojiReacts } = useFeatures(); + const dispatch = useAppDispatch(); const [expanded, setExpanded] = useState(false); const { x, y, strategy, refs, update } = useFloating({ @@ -75,7 +79,18 @@ const EmojiSelector: React.FC = ({ }); 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 = ({ }, [visible]); useClickOutside(refs, () => { - if (onClose) { - onClose(); - } + onClose?.(); }); return ( diff --git a/src/components/ui/modal.tsx b/src/components/ui/modal.tsx index 595787da6..a7db84f99 100644 --- a/src/components/ui/modal.tsx +++ b/src/components/ui/modal.tsx @@ -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(({
diff --git a/src/features/emoji/components/emoji-picker-dropdown.tsx b/src/features/emoji/components/emoji-picker-dropdown.tsx index ae5231fb5..df456925a 100644 --- a/src/features/emoji/components/emoji-picker-dropdown.tsx +++ b/src/features/emoji/components/emoji-picker-dropdown.tsx @@ -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 = ({ children, update }) => { const [nextTick, setNextTick] = useState(false); useEffect(() => { @@ -125,7 +130,7 @@ const RenderAfter = ({ children, update }: any) => { }; const EmojiPickerDropdown: React.FC = ({ - 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 = ({ 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 = ({ document.body.style.overflow = ''; }, []); + if (!visible) { + return null; + } + return ( - visible ? ( - - - - - - ) : null + + {})}> + + + ); }; diff --git a/src/features/emoji/components/emoji-picker.tsx b/src/features/emoji/components/emoji-picker.tsx index 927f010ba..a6dc860ff 100644 --- a/src/features/emoji/components/emoji-picker.tsx +++ b/src/features/emoji/components/emoji-picker.tsx @@ -19,7 +19,7 @@ const Picker: React.FC = (props) => { new EmojiPicker(input); }, []); - return
; + return
; }; export default Picker; diff --git a/src/features/ui/components/modal-root.tsx b/src/features/ui/components/modal-root.tsx index 5eff12842..024b71d8b 100644 --- a/src/features/ui/components/modal-root.tsx +++ b/src/features/ui/components/modal-root.tsx @@ -18,6 +18,7 @@ import { EditDomainModal, EditFederationModal, EmbedModal, + EmojiPickerModal, EventMapModal, EventParticipantsModal, FamiliarFollowersModal, @@ -72,6 +73,7 @@ const MODAL_COMPONENTS: Record> = { 'EDIT_FEDERATION': EditFederationModal, 'EDIT_RULE': EditRuleModal, 'EMBED': EmbedModal, + 'EMOJI_PICKER': EmojiPickerModal, 'EVENT_MAP': EventMapModal, 'EVENT_PARTICIPANTS': EventParticipantsModal, 'FAMILIAR_FOLLOWERS': FamiliarFollowersModal, diff --git a/src/features/ui/components/modals/emoji-picker-modal.tsx b/src/features/ui/components/modals/emoji-picker-modal.tsx new file mode 100644 index 000000000..e3cd91ccc --- /dev/null +++ b/src/features/ui/components/modals/emoji-picker-modal.tsx @@ -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 = (props) => { + return ( + + + + ); +}; + +export default EmojiPickerModal; \ No newline at end of file diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index 546e931ee..7b2a62ab5 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -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'));