mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-09-13 22:32:26 +03:00
separate emojis and sticker groups logic
This commit is contained in:
parent
778c3f2eb3
commit
cd963d91d3
1 changed files with 187 additions and 191 deletions
|
@ -4,7 +4,6 @@ import React, {
|
||||||
MouseEventHandler,
|
MouseEventHandler,
|
||||||
UIEventHandler,
|
UIEventHandler,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
memo,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
@ -15,10 +14,10 @@ import FocusTrap from 'focus-trap-react';
|
||||||
import { isKeyHotkey } from 'is-hotkey';
|
import { isKeyHotkey } from 'is-hotkey';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { MatrixClient, Room } from 'matrix-js-sdk';
|
import { MatrixClient, Room } from 'matrix-js-sdk';
|
||||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
import { Atom, atom, useAtomValue, useSetAtom } from 'jotai';
|
||||||
|
|
||||||
import * as css from './EmojiBoard.css';
|
import * as css from './EmojiBoard.css';
|
||||||
import { EmojiGroupId, IEmoji, IEmojiGroup, emojiGroups, emojis } from '../../plugins/emoji';
|
import { IEmoji, IEmojiGroup, emojiGroups, emojis } from '../../plugins/emoji';
|
||||||
import { IEmojiGroupLabels, useEmojiGroupLabels } from './useEmojiGroupLabels';
|
import { IEmojiGroupLabels, useEmojiGroupLabels } from './useEmojiGroupLabels';
|
||||||
import { IEmojiGroupIcons, useEmojiGroupIcons } from './useEmojiGroupIcons';
|
import { IEmojiGroupIcons, useEmojiGroupIcons } from './useEmojiGroupIcons';
|
||||||
import { preventScrollWithArrowKey, stopPropagation } from '../../utils/keyboard';
|
import { preventScrollWithArrowKey, stopPropagation } from '../../utils/keyboard';
|
||||||
|
@ -129,109 +128,120 @@ export const EmojiGroup = as<
|
||||||
</Box>
|
</Box>
|
||||||
));
|
));
|
||||||
|
|
||||||
function RecentEmojiSidebarStack({ onItemClick }: { onItemClick: (id: string) => void }) {
|
type EmojiSidebarProps = {
|
||||||
const activeGroupId = useAtomValue(activeGroupIdAtom);
|
activeGroupAtom: Atom<string | undefined>;
|
||||||
|
handleOpenGroup: (groupId: string) => void;
|
||||||
return (
|
|
||||||
<SidebarStack>
|
|
||||||
<GroupIcon
|
|
||||||
active={activeGroupId === RECENT_GROUP_ID}
|
|
||||||
id={RECENT_GROUP_ID}
|
|
||||||
label="Recent"
|
|
||||||
icon={Icons.RecentClock}
|
|
||||||
onClick={onItemClick}
|
|
||||||
/>
|
|
||||||
</SidebarStack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ImagePackSidebarStack({
|
|
||||||
mx,
|
|
||||||
packs,
|
|
||||||
usage,
|
|
||||||
onItemClick,
|
|
||||||
useAuthentication,
|
|
||||||
}: {
|
|
||||||
mx: MatrixClient;
|
|
||||||
packs: ImagePack[];
|
packs: ImagePack[];
|
||||||
usage: ImageUsage;
|
|
||||||
onItemClick: (id: string) => void;
|
|
||||||
useAuthentication?: boolean;
|
|
||||||
}) {
|
|
||||||
const activeGroupId = useAtomValue(activeGroupIdAtom);
|
|
||||||
return (
|
|
||||||
<SidebarStack>
|
|
||||||
{usage === ImageUsage.Emoticon && <SidebarDivider />}
|
|
||||||
{packs.map((pack) => {
|
|
||||||
let label = pack.meta.name;
|
|
||||||
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name;
|
|
||||||
|
|
||||||
const url =
|
|
||||||
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) || pack.meta.avatar;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ImageGroupIcon
|
|
||||||
key={pack.id}
|
|
||||||
active={activeGroupId === pack.id}
|
|
||||||
id={pack.id}
|
|
||||||
label={label ?? 'Unknown Pack'}
|
|
||||||
url={url}
|
|
||||||
onClick={onItemClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</SidebarStack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NativeEmojiSidebarStack({
|
|
||||||
groups,
|
|
||||||
icons,
|
|
||||||
labels,
|
|
||||||
onItemClick,
|
|
||||||
}: {
|
|
||||||
groups: IEmojiGroup[];
|
groups: IEmojiGroup[];
|
||||||
icons: IEmojiGroupIcons;
|
icons: IEmojiGroupIcons;
|
||||||
labels: IEmojiGroupLabels;
|
labels: IEmojiGroupLabels;
|
||||||
onItemClick: (id: EmojiGroupId) => void;
|
};
|
||||||
}) {
|
function EmojiSidebar({
|
||||||
const activeGroupId = useAtomValue(activeGroupIdAtom);
|
activeGroupAtom,
|
||||||
|
handleOpenGroup,
|
||||||
|
packs,
|
||||||
|
groups,
|
||||||
|
icons,
|
||||||
|
labels,
|
||||||
|
}: EmojiSidebarProps) {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
|
||||||
|
const activeGroupId = useAtomValue(activeGroupAtom);
|
||||||
|
const usage = ImageUsage.Emoticon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarStack className={css.NativeEmojiSidebarStack}>
|
<Sidebar>
|
||||||
<SidebarDivider />
|
<SidebarStack>
|
||||||
{groups.map((group) => (
|
|
||||||
<GroupIcon
|
<GroupIcon
|
||||||
key={group.id}
|
active={activeGroupId === RECENT_GROUP_ID}
|
||||||
active={activeGroupId === group.id}
|
id={RECENT_GROUP_ID}
|
||||||
id={group.id}
|
label="Recent"
|
||||||
label={labels[group.id]}
|
icon={Icons.RecentClock}
|
||||||
icon={icons[group.id]}
|
onClick={handleOpenGroup}
|
||||||
onClick={onItemClick}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</SidebarStack>
|
||||||
</SidebarStack>
|
{packs.length > 0 && (
|
||||||
|
<SidebarStack>
|
||||||
|
<SidebarDivider />
|
||||||
|
{packs.map((pack) => {
|
||||||
|
let label = pack.meta.name;
|
||||||
|
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name;
|
||||||
|
|
||||||
|
const url =
|
||||||
|
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) ||
|
||||||
|
pack.meta.avatar;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageGroupIcon
|
||||||
|
key={pack.id}
|
||||||
|
active={activeGroupId === pack.id}
|
||||||
|
id={pack.id}
|
||||||
|
label={label ?? 'Unknown Pack'}
|
||||||
|
url={url}
|
||||||
|
onClick={handleOpenGroup}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SidebarStack>
|
||||||
|
)}
|
||||||
|
<SidebarStack className={css.NativeEmojiSidebarStack}>
|
||||||
|
<SidebarDivider />
|
||||||
|
{groups.map((group) => (
|
||||||
|
<GroupIcon
|
||||||
|
key={group.id}
|
||||||
|
active={activeGroupId === group.id}
|
||||||
|
id={group.id}
|
||||||
|
label={labels[group.id]}
|
||||||
|
icon={icons[group.id]}
|
||||||
|
onClick={handleOpenGroup}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SidebarStack>
|
||||||
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RecentEmojiGroup({
|
type StickerSidebarProps = {
|
||||||
label,
|
activeGroupAtom: Atom<string | undefined>;
|
||||||
id,
|
handleOpenGroup: (groupId: string) => void;
|
||||||
emojis: recentEmojis,
|
packs: ImagePack[];
|
||||||
}: {
|
};
|
||||||
label: string;
|
function StickerSidebar({ activeGroupAtom, handleOpenGroup, packs }: StickerSidebarProps) {
|
||||||
id: string;
|
const mx = useMatrixClient();
|
||||||
emojis: IEmoji[];
|
const useAuthentication = useMediaAuthentication();
|
||||||
}) {
|
|
||||||
|
const activeGroupId = useAtomValue(activeGroupAtom);
|
||||||
|
const usage = ImageUsage.Sticker;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EmojiGroup key={id} id={id} label={label}>
|
<Sidebar>
|
||||||
{recentEmojis.map((emoji) => (
|
<SidebarStack>
|
||||||
<EmojiItem key={emoji.shortcode} emoji={emoji} />
|
<SidebarDivider />
|
||||||
))}
|
{packs.map((pack) => {
|
||||||
</EmojiGroup>
|
let label = pack.meta.name;
|
||||||
|
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name;
|
||||||
|
|
||||||
|
const url =
|
||||||
|
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) || pack.meta.avatar;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageGroupIcon
|
||||||
|
key={pack.id}
|
||||||
|
active={activeGroupId === pack.id}
|
||||||
|
id={pack.id}
|
||||||
|
label={label ?? 'Unknown Pack'}
|
||||||
|
url={url}
|
||||||
|
onClick={handleOpenGroup}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SidebarStack>
|
||||||
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchEmojiGroup({
|
export function SearchGroup({
|
||||||
mx,
|
mx,
|
||||||
tab,
|
tab,
|
||||||
label,
|
label,
|
||||||
|
@ -275,18 +285,51 @@ export function SearchEmojiGroup({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CustomEmojiGroups = memo(
|
type StickerGroupsProps = {
|
||||||
({
|
packs: ImagePack[];
|
||||||
mx,
|
};
|
||||||
groups,
|
function StickerGroups({ packs }: StickerGroupsProps) {
|
||||||
useAuthentication,
|
const mx = useMatrixClient();
|
||||||
}: {
|
const useAuthentication = useMediaAuthentication();
|
||||||
mx: MatrixClient;
|
|
||||||
groups: ImagePack[];
|
if (packs.length === 0) {
|
||||||
useAuthentication?: boolean;
|
return <NoStickerPacks />;
|
||||||
}) => (
|
}
|
||||||
|
return packs.map((pack) => (
|
||||||
|
<EmojiGroup key={pack.id} id={pack.id} label={pack.meta.name || 'Unknown'}>
|
||||||
|
{pack
|
||||||
|
.getImages(ImageUsage.Sticker)
|
||||||
|
.sort((a, b) => a.shortcode.localeCompare(b.shortcode))
|
||||||
|
.map((image) => (
|
||||||
|
<StickerItem
|
||||||
|
key={image.shortcode}
|
||||||
|
mx={mx}
|
||||||
|
useAuthentication={useAuthentication}
|
||||||
|
image={image}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</EmojiGroup>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmojiGroupsProps = {
|
||||||
|
recentEmojis: IEmoji[];
|
||||||
|
packs: ImagePack[];
|
||||||
|
groups: IEmojiGroup[];
|
||||||
|
labels: IEmojiGroupLabels;
|
||||||
|
};
|
||||||
|
export function EmojiGroups({ recentEmojis, packs, groups, labels }: EmojiGroupsProps) {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const useAuthentication = useMediaAuthentication();
|
||||||
|
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
{groups.map((pack) => (
|
<EmojiGroup id={RECENT_GROUP_ID} label="Recent">
|
||||||
|
{recentEmojis.map((emoji) => (
|
||||||
|
<EmojiItem key={emoji.shortcode} emoji={emoji} />
|
||||||
|
))}
|
||||||
|
</EmojiGroup>
|
||||||
|
{packs.map((pack) => (
|
||||||
<EmojiGroup key={pack.id} id={pack.id} label={pack.meta.name || 'Unknown'}>
|
<EmojiGroup key={pack.id} id={pack.id} label={pack.meta.name || 'Unknown'}>
|
||||||
{pack
|
{pack
|
||||||
.getImages(ImageUsage.Emoticon)
|
.getImages(ImageUsage.Emoticon)
|
||||||
|
@ -301,44 +344,6 @@ export const CustomEmojiGroups = memo(
|
||||||
))}
|
))}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
))}
|
))}
|
||||||
</>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StickerGroups = memo(
|
|
||||||
({
|
|
||||||
mx,
|
|
||||||
groups,
|
|
||||||
useAuthentication,
|
|
||||||
}: {
|
|
||||||
mx: MatrixClient;
|
|
||||||
groups: ImagePack[];
|
|
||||||
useAuthentication?: boolean;
|
|
||||||
}) =>
|
|
||||||
groups.length === 0 ? (
|
|
||||||
<NoStickerPacks />
|
|
||||||
) : (
|
|
||||||
groups.map((pack) => (
|
|
||||||
<EmojiGroup key={pack.id} id={pack.id} label={pack.meta.name || 'Unknown'}>
|
|
||||||
{pack
|
|
||||||
.getImages(ImageUsage.Sticker)
|
|
||||||
.sort((a, b) => a.shortcode.localeCompare(b.shortcode))
|
|
||||||
.map((image) => (
|
|
||||||
<StickerItem
|
|
||||||
key={image.shortcode}
|
|
||||||
mx={mx}
|
|
||||||
useAuthentication={useAuthentication}
|
|
||||||
image={image}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</EmojiGroup>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const NativeEmojiGroups = memo(
|
|
||||||
({ groups, labels }: { groups: IEmojiGroup[]; labels: IEmojiGroupLabels }) => (
|
|
||||||
<>
|
|
||||||
{groups.map((emojiGroup) => (
|
{groups.map((emojiGroup) => (
|
||||||
<EmojiGroup key={emojiGroup.id} id={emojiGroup.id} label={labels[emojiGroup.id]}>
|
<EmojiGroup key={emojiGroup.id} id={emojiGroup.id} label={labels[emojiGroup.id]}>
|
||||||
{emojiGroup.emojis.map((emoji) => (
|
{emojiGroup.emojis.map((emoji) => (
|
||||||
|
@ -347,8 +352,8 @@ export const NativeEmojiGroups = memo(
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
const DefaultEmojiPreview: PreviewData = { key: '🙂', shortcode: 'slight_smile' };
|
const DefaultEmojiPreview: PreviewData = { key: '🙂', shortcode: 'slight_smile' };
|
||||||
|
|
||||||
|
@ -359,6 +364,19 @@ const SEARCH_OPTIONS: UseAsyncSearchOptions = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type EmojiBoardProps = {
|
||||||
|
tab?: EmojiBoardTab;
|
||||||
|
onTabChange?: (tab: EmojiBoardTab) => void;
|
||||||
|
imagePackRooms: Room[];
|
||||||
|
requestClose: () => void;
|
||||||
|
returnFocusOnDeactivate?: boolean;
|
||||||
|
onEmojiSelect?: (unicode: string, shortcode: string) => void;
|
||||||
|
onCustomEmojiSelect?: (mxc: string, shortcode: string) => void;
|
||||||
|
onStickerSelect?: (mxc: string, shortcode: string, label: string) => void;
|
||||||
|
allowTextCustomEmoji?: boolean;
|
||||||
|
addToRecentEmoji?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export function EmojiBoard({
|
export function EmojiBoard({
|
||||||
tab = EmojiBoardTab.Emoji,
|
tab = EmojiBoardTab.Emoji,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
|
@ -370,20 +388,8 @@ export function EmojiBoard({
|
||||||
onStickerSelect,
|
onStickerSelect,
|
||||||
allowTextCustomEmoji,
|
allowTextCustomEmoji,
|
||||||
addToRecentEmoji = true,
|
addToRecentEmoji = true,
|
||||||
}: {
|
}: EmojiBoardProps) {
|
||||||
tab?: EmojiBoardTab;
|
|
||||||
onTabChange?: (tab: EmojiBoardTab) => void;
|
|
||||||
imagePackRooms: Room[];
|
|
||||||
requestClose: () => void;
|
|
||||||
returnFocusOnDeactivate?: boolean;
|
|
||||||
onEmojiSelect?: (unicode: string, shortcode: string) => void;
|
|
||||||
onCustomEmojiSelect?: (mxc: string, shortcode: string) => void;
|
|
||||||
onStickerSelect?: (mxc: string, shortcode: string, label: string) => void;
|
|
||||||
allowTextCustomEmoji?: boolean;
|
|
||||||
addToRecentEmoji?: boolean;
|
|
||||||
}) {
|
|
||||||
const emojiTab = tab === EmojiBoardTab.Emoji;
|
const emojiTab = tab === EmojiBoardTab.Emoji;
|
||||||
const stickerTab = tab === EmojiBoardTab.Sticker;
|
|
||||||
const usage = emojiTab ? ImageUsage.Emoticon : ImageUsage.Sticker;
|
const usage = emojiTab ? ImageUsage.Emoticon : ImageUsage.Sticker;
|
||||||
|
|
||||||
const previewAtom = useMemo(
|
const previewAtom = useMemo(
|
||||||
|
@ -541,28 +547,22 @@ export function EmojiBoard({
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
sidebar={
|
sidebar={
|
||||||
<Sidebar>
|
emojiTab ? (
|
||||||
{emojiTab && recentEmojis.length > 0 && (
|
<EmojiSidebar
|
||||||
<RecentEmojiSidebarStack onItemClick={handleScrollToGroup} />
|
activeGroupAtom={activeGroupIdAtom}
|
||||||
)}
|
handleOpenGroup={handleScrollToGroup}
|
||||||
{imagePacks.length > 0 && (
|
packs={imagePacks}
|
||||||
<ImagePackSidebarStack
|
groups={emojiGroups}
|
||||||
mx={mx}
|
icons={emojiGroupIcons}
|
||||||
usage={usage}
|
labels={emojiGroupLabels}
|
||||||
packs={imagePacks}
|
/>
|
||||||
onItemClick={handleScrollToGroup}
|
) : (
|
||||||
useAuthentication={useAuthentication}
|
<StickerSidebar
|
||||||
/>
|
activeGroupAtom={activeGroupIdAtom}
|
||||||
)}
|
handleOpenGroup={handleScrollToGroup}
|
||||||
{emojiTab && (
|
packs={imagePacks}
|
||||||
<NativeEmojiSidebarStack
|
/>
|
||||||
groups={emojiGroups}
|
)
|
||||||
icons={emojiGroupIcons}
|
|
||||||
labels={emojiGroupLabels}
|
|
||||||
onItemClick={handleScrollToGroup}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Sidebar>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
|
@ -581,7 +581,7 @@ export function EmojiBoard({
|
||||||
gap="200"
|
gap="200"
|
||||||
>
|
>
|
||||||
{searchedItems && (
|
{searchedItems && (
|
||||||
<SearchEmojiGroup
|
<SearchGroup
|
||||||
mx={mx}
|
mx={mx}
|
||||||
tab={tab}
|
tab={tab}
|
||||||
id={SEARCH_GROUP_ID}
|
id={SEARCH_GROUP_ID}
|
||||||
|
@ -590,20 +590,16 @@ export function EmojiBoard({
|
||||||
useAuthentication={useAuthentication}
|
useAuthentication={useAuthentication}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{emojiTab && recentEmojis.length > 0 && (
|
{emojiTab ? (
|
||||||
<RecentEmojiGroup id={RECENT_GROUP_ID} label="Recent" emojis={recentEmojis} />
|
<EmojiGroups
|
||||||
)}
|
recentEmojis={recentEmojis}
|
||||||
{emojiTab && (
|
packs={imagePacks}
|
||||||
<CustomEmojiGroups
|
groups={emojiGroups}
|
||||||
mx={mx}
|
labels={emojiGroupLabels}
|
||||||
groups={imagePacks}
|
|
||||||
useAuthentication={useAuthentication}
|
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<StickerGroups packs={imagePacks} />
|
||||||
)}
|
)}
|
||||||
{stickerTab && (
|
|
||||||
<StickerGroups mx={mx} groups={imagePacks} useAuthentication={useAuthentication} />
|
|
||||||
)}
|
|
||||||
{emojiTab && <NativeEmojiGroups groups={emojiGroups} labels={emojiGroupLabels} />}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue