From 7382171650c2ce989f6396d799a431508e31d05b Mon Sep 17 00:00:00 2001 From: ChaosExAnima Date: Thu, 13 Nov 2025 16:20:07 +0100 Subject: [PATCH] sets up settings page --- .../components/emoji/picker/group-button.tsx | 71 ++++++++++ .../mastodon/components/emoji/picker/hooks.ts | 40 ++++++ .../components/emoji/picker/index.tsx | 121 ++++-------------- .../components/emoji/picker/settings.tsx | 36 ++++++ .../components/emoji/picker/styles.module.css | 8 +- 5 files changed, 178 insertions(+), 98 deletions(-) create mode 100644 app/javascript/mastodon/components/emoji/picker/group-button.tsx create mode 100644 app/javascript/mastodon/components/emoji/picker/hooks.ts create mode 100644 app/javascript/mastodon/components/emoji/picker/settings.tsx diff --git a/app/javascript/mastodon/components/emoji/picker/group-button.tsx b/app/javascript/mastodon/components/emoji/picker/group-button.tsx new file mode 100644 index 00000000000..ece938b2bb1 --- /dev/null +++ b/app/javascript/mastodon/components/emoji/picker/group-button.tsx @@ -0,0 +1,71 @@ +import type { FC, MouseEventHandler } from 'react'; +import { useCallback, useState, useEffect } from 'react'; + +import classNames from 'classnames'; + +import { loadUnicodeEmojiGroupIcon } from '@/mastodon/features/emoji/database'; +import { useEmojiAppState } from '@/mastodon/features/emoji/mode'; +import type { AnyEmojiData } from '@/mastodon/features/emoji/types'; + +import { Emoji } from '..'; + +import { mockCustomEmojis, groupKeysToNumber } from './constants'; +import classes from './styles.module.css'; + +interface PickerGroupButtonProps { + onSelect: (key: string) => void; + group: string; + message: string; + disabled?: boolean; +} + +export const PickerGroupButton: FC = ({ + onSelect, + message, + group, + disabled = false, +}) => { + const handleClick: MouseEventHandler = useCallback( + (event) => { + event.preventDefault(); + onSelect(group); + }, + [onSelect, group], + ); + const { currentLocale } = useEmojiAppState(); + const [icon, setIcon] = useState(() => { + const emoji = mockCustomEmojis.find((emoji) => emoji.category === group); + return emoji ?? null; + }); + + useEffect(() => { + if (icon !== null) { + return; + } + + if (group in groupKeysToNumber) { + const groupNum = groupKeysToNumber[group]; + if (typeof groupNum !== 'undefined') { + void loadUnicodeEmojiGroupIcon(groupNum, currentLocale).then(setIcon); + } + } + }, [currentLocale, group, icon]); + + return ( +
  • + +
  • + ); +}; diff --git a/app/javascript/mastodon/components/emoji/picker/hooks.ts b/app/javascript/mastodon/components/emoji/picker/hooks.ts new file mode 100644 index 00000000000..0852cd12c1e --- /dev/null +++ b/app/javascript/mastodon/components/emoji/picker/hooks.ts @@ -0,0 +1,40 @@ +import { useMemo, useState } from 'react'; + +import { usePrevious } from '@dnd-kit/utilities'; +import type { MessagesDataset } from 'emojibase'; +import enMessages from 'emojibase-data/en/messages.json'; + +import { useEmojiAppState } from '@/mastodon/features/emoji/mode'; + +import { groupsToHide } from './constants'; + +export function useLocaleMessages() { + const { currentLocale } = useEmojiAppState(); + // This isn't needed in real life, as the current locale is only set on page load. + // However it Storybook can update the locale without a refresh. + const prevLocale = usePrevious(currentLocale); + + const [messages, setMessages] = useState(enMessages); + if (prevLocale !== currentLocale) { + // This is messy, but it's just for the mock picker. + import( + `../../../../../../node_modules/emojibase-data/${currentLocale}/messages.json` + ) + .then((module: { default: MessagesDataset }) => { + setMessages(module.default); + }) + .catch((err: unknown) => { + console.warn('fell back to en messages', err); + }); + } + + const groups = useMemo( + () => messages.groups.filter((group) => !groupsToHide.includes(group.key)), + [messages.groups], + ); + + return { + ...messages, + groups, + }; +} diff --git a/app/javascript/mastodon/components/emoji/picker/index.tsx b/app/javascript/mastodon/components/emoji/picker/index.tsx index 1cd71a4358f..dcb49488171 100644 --- a/app/javascript/mastodon/components/emoji/picker/index.tsx +++ b/app/javascript/mastodon/components/emoji/picker/index.tsx @@ -1,26 +1,19 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import type { FC, MouseEventHandler } from 'react'; -import type { GroupMessage, MessagesDataset } from 'emojibase'; -import messages from 'emojibase-data/en/messages.json'; +import classNames from 'classnames'; -import { loadUnicodeEmojiGroupIcon } from '@/mastodon/features/emoji/database'; -import { useEmojiAppState } from '@/mastodon/features/emoji/mode'; import type { AnyEmojiData } from '@/mastodon/features/emoji/types'; -import { usePrevious } from '@/mastodon/hooks/usePrevious'; import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; -import { Emoji } from '..'; import { IconButton } from '../../icon_button'; import { CustomEmojiProvider } from '../context'; -import { - groupKeysToNumber, - groupsToHide, - mockCustomEmojis, - mockCustomGroups, -} from './constants'; +import { mockCustomEmojis, mockCustomGroups } from './constants'; +import { PickerGroupButton } from './group-button'; +import { useLocaleMessages } from './hooks'; import { PickerGroupList } from './list'; +import { PickerSettings } from './settings'; import classes from './styles.module.css'; interface MockEmojiPickerProps { @@ -39,6 +32,12 @@ export const MockEmojiPicker: FC = ({ onSelect }) => { [onSelect], ); + const [showSettings, setShowSettings] = useState(false); + const handleSettingsClick: MouseEventHandler = useCallback((event) => { + event.preventDefault(); + setShowSettings((prev) => !prev); + }, []); + const wrapperRef = useRef(null); const handleGroupSelect = useCallback((key: string) => { const wrapper = wrapperRef.current; @@ -58,35 +57,7 @@ export const MockEmojiPicker: FC = ({ onSelect }) => { } }, []); - const [showSettings, setShowSettings] = useState(false); - const handleSettingsClick: MouseEventHandler = useCallback((event) => { - event.preventDefault(); - setShowSettings((prev) => !prev); - }, []); - - const { currentLocale } = useEmojiAppState(); - // This isn't needed in real life, as the current locale is only set on page load. - const prevLocale = usePrevious(currentLocale); - const [groups, setGroups] = useState([]); - if (prevLocale !== currentLocale) { - // This is messy, but it's just for the mock picker. - import( - `../../../../../../node_modules/emojibase-data/${currentLocale}/messages.json` - ) - .then((module: { default: MessagesDataset }) => { - setGroups( - module.default.groups.filter( - (group) => !groupsToHide.includes(group.key), - ), - ); - }) - .catch((err: unknown) => { - console.warn('fell back to en messages', err); - setGroups( - messages.groups.filter((group) => !groupsToHide.includes(group.key)), - ); - }); - } + const { groups } = useLocaleMessages(); return ( @@ -104,7 +75,7 @@ export const MockEmojiPicker: FC = ({ onSelect }) => { onClick={handleSettingsClick} /> - {showSettings &&
    Settings here
    } + {showSettings && } {!showSettings && (
    {mockCustomGroups.map((group) => ( @@ -125,22 +96,29 @@ export const MockEmojiPicker: FC = ({ onSelect }) => { ))}
    )} -
      +
        {mockCustomGroups.map((group) => ( - ))}
      • {groups.map((group) => ( - ))}
      @@ -148,54 +126,3 @@ export const MockEmojiPicker: FC = ({ onSelect }) => { ); }; - -interface PickerNavProps { - onSelect: (key: string) => void; - group: string; - message: string; -} - -const PickerNavButton: FC = ({ onSelect, message, group }) => { - const handleClick: MouseEventHandler = useCallback( - (event) => { - event.preventDefault(); - onSelect(group); - }, - [onSelect, group], - ); - const { currentLocale } = useEmojiAppState(); - const [icon, setIcon] = useState(() => { - const emoji = mockCustomEmojis.find((emoji) => emoji.category === group); - return emoji ?? null; - }); - - useEffect(() => { - if (icon !== null) { - return; - } - - if (group in groupKeysToNumber) { - const groupNum = groupKeysToNumber[group]; - if (typeof groupNum !== 'undefined') { - void loadUnicodeEmojiGroupIcon(groupNum, currentLocale).then(setIcon); - } - } - }, [currentLocale, group, icon]); - - return ( -
    • - -
    • - ); -}; diff --git a/app/javascript/mastodon/components/emoji/picker/settings.tsx b/app/javascript/mastodon/components/emoji/picker/settings.tsx new file mode 100644 index 00000000000..a3b2a12db94 --- /dev/null +++ b/app/javascript/mastodon/components/emoji/picker/settings.tsx @@ -0,0 +1,36 @@ +import type { FC } from 'react'; + +import type { SkinToneKey } from 'emojibase'; + +import { useLocaleMessages } from './hooks'; +import classes from './styles.module.css'; + +const toneToEmoji: Record = { + light: '👋🏻', + 'medium-light': '👋🏼', + medium: '👋🏽', + 'medium-dark': '👋🏾', + dark: '👋🏿', +}; + +export const PickerSettings: FC = () => { + const { skinTones } = useLocaleMessages(); + return ( +
      +

      Emoji Settings

      + +
      + ); +}; diff --git a/app/javascript/mastodon/components/emoji/picker/styles.module.css b/app/javascript/mastodon/components/emoji/picker/styles.module.css index d595c1edfb6..9ca8efa4485 100644 --- a/app/javascript/mastodon/components/emoji/picker/styles.module.css +++ b/app/javascript/mastodon/components/emoji/picker/styles.module.css @@ -1,5 +1,6 @@ .wrapper { --max-width: 20rem; + --col-count: 6; display: grid; gap: 0.5rem; @@ -92,7 +93,7 @@ .emojiGrid { display: grid; - grid-template-columns: repeat(6, 1fr); + grid-template-columns: repeat(var(--col-count), 1fr); gap: 0.5rem; } @@ -145,6 +146,11 @@ height: 2rem; border-radius: 50%; border: 1px solid gray; + + .settingsNav & { + opacity: 0.5; + pointer-events: none; + } } .separator {