mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(app): keep emoji sprites aligned across zoom levels (#1557)
This commit is contained in:
1 parent
06243c48d2
commit
0b08e1de2c
6 files changed
+24
-51
No files matched your search
@@ -5,7 +5,6 @@ import {useSearchInputAutofocus} from '@app/features/app/hooks/useSearchInputAut
|
||||
import {useShouldAnimate} from '@app/features/app/hooks/useShouldAnimate';
|
||||
import styles from '@app/features/channel/components/EmojiPicker.module.css';
|
||||
import {EmojiPickerCategoryList} from '@app/features/channel/components/emoji_picker/EmojiPickerCategoryList';
|
||||
import {EMOJI_SPRITE_SIZE} from '@app/features/channel/components/emoji_picker/EmojiPickerConstants';
|
||||
import {EmojiPickerInspector} from '@app/features/channel/components/emoji_picker/EmojiPickerInspector';
|
||||
import {EmojiPickerSearchBar} from '@app/features/channel/components/emoji_picker/EmojiPickerSearchBar';
|
||||
import {useEmojiCategories} from '@app/features/channel/components/emoji_picker/hooks/useEmojiCategories';
|
||||
@@ -26,7 +25,6 @@ import {
|
||||
shouldShowEmojiPremiumUpsell,
|
||||
} from '@app/features/expressions/utils/ExpressionPermissionUtils';
|
||||
import {getEmojiDisplayDataWithSkinTone} from '@app/features/expressions/utils/SkinToneUtils';
|
||||
import UnicodeEmojis, {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import Permission from '@app/features/permissions/state/Permission';
|
||||
import {ComponentDispatch} from '@app/features/platform/utils/ComponentBus';
|
||||
import {usePremiumUpsellData} from '@app/features/premium/hooks/usePremiumUpsellData';
|
||||
@@ -107,17 +105,6 @@ export const EmojiPicker = observer(
|
||||
},
|
||||
[shouldAnimateEmoji, skinTone],
|
||||
);
|
||||
const spriteSheetSizes = useMemo(() => {
|
||||
const nonDiversitySize = [
|
||||
`${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.NonDiversityPerRow}px`,
|
||||
`${EMOJI_SPRITE_SIZE * Math.ceil(UnicodeEmojis.numNonDiversitySprites / EMOJI_SPRITES.NonDiversityPerRow)}px`,
|
||||
].join(' ');
|
||||
const diversitySize = [
|
||||
`${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.DiversityPerRow}px`,
|
||||
`${EMOJI_SPRITE_SIZE * Math.ceil(UnicodeEmojis.numDiversitySprites / EMOJI_SPRITES.DiversityPerRow)}px`,
|
||||
].join(' ');
|
||||
return {nonDiversitySize, diversitySize};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const handleEmojiDataUpdated = () => {
|
||||
setEmojiDataVersion((version) => version + 1);
|
||||
@@ -324,7 +311,6 @@ export const EmojiPicker = observer(
|
||||
handleHover={handleHover}
|
||||
handleSelect={handleEmojiSelect}
|
||||
skinTone={skinTone}
|
||||
spriteSheetSizes={spriteSheetSizes}
|
||||
channel={channel}
|
||||
allowAnimation={shouldAnimateEmoji}
|
||||
hoveredEmoji={hoveredEmoji}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {PREMIUM_PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstant
|
||||
import {useSearchInputAutofocus} from '@app/features/app/hooks/useSearchInputAutofocus';
|
||||
import {useShouldAnimate} from '@app/features/app/hooks/useShouldAnimate';
|
||||
import {EmojiPickerCategoryList} from '@app/features/channel/components/emoji_picker/EmojiPickerCategoryList';
|
||||
import {EMOJI_SPRITE_SIZE} from '@app/features/channel/components/emoji_picker/EmojiPickerConstants';
|
||||
import {EmojiPickerSearchBar} from '@app/features/channel/components/emoji_picker/EmojiPickerSearchBar';
|
||||
import {useEmojiCategories} from '@app/features/channel/components/emoji_picker/hooks/useEmojiCategories';
|
||||
import {useVirtualRows} from '@app/features/channel/components/emoji_picker/hooks/useVirtualRows';
|
||||
@@ -26,7 +25,6 @@ import {
|
||||
shouldShowEmojiPremiumUpsell,
|
||||
} from '@app/features/expressions/utils/ExpressionPermissionUtils';
|
||||
import {getEmojiDisplayDataWithSkinTone} from '@app/features/expressions/utils/SkinToneUtils';
|
||||
import UnicodeEmojis, {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import Permission from '@app/features/permissions/state/Permission';
|
||||
import {ComponentDispatch} from '@app/features/platform/utils/ComponentBus';
|
||||
import {usePremiumUpsellData} from '@app/features/premium/hooks/usePremiumUpsellData';
|
||||
@@ -80,17 +78,6 @@ export const MobileEmojiPicker = observer(
|
||||
const getEmojiGuildId = useCallback((emoji: FlatEmoji) => emoji.guildId, []);
|
||||
const skinTone = Emoji.skinTone;
|
||||
const shouldAnimateEmoji = useShouldAnimate({kind: 'emoji'});
|
||||
const spriteSheetSizes = useMemo(() => {
|
||||
const nonDiversitySize = [
|
||||
`${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.NonDiversityPerRow}px`,
|
||||
`${EMOJI_SPRITE_SIZE * Math.ceil(UnicodeEmojis.numNonDiversitySprites / EMOJI_SPRITES.NonDiversityPerRow)}px`,
|
||||
].join(' ');
|
||||
const diversitySize = [
|
||||
`${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.DiversityPerRow}px`,
|
||||
`${EMOJI_SPRITE_SIZE * Math.ceil(UnicodeEmojis.numDiversitySprites / EMOJI_SPRITES.DiversityPerRow)}px`,
|
||||
].join(' ');
|
||||
return {nonDiversitySize, diversitySize};
|
||||
}, []);
|
||||
const searchTerm = externalSearchTerm ?? internalSearchTerm;
|
||||
const setSearchTerm = externalSetSearchTerm ?? setInternalSearchTerm;
|
||||
const normalizedSearchTerm = useMemo(() => normalizeEmojiSearchQuery(searchTerm), [searchTerm]);
|
||||
@@ -296,7 +283,6 @@ export const MobileEmojiPicker = observer(
|
||||
handleHover={handleHover}
|
||||
handleSelect={handleSelect}
|
||||
skinTone={skinTone}
|
||||
spriteSheetSizes={spriteSheetSizes}
|
||||
channel={channel}
|
||||
allowAnimation={shouldAnimateEmoji}
|
||||
gridColumns={gridColumns}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {convertToCodePoints} from '@app/features/expressions/utils/EmojiCodepointUtils';
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import sprite1f3fb2x from '@app/media/images/emoji-sprites/[email protected]';
|
||||
import sprite1f3fb1x from '@app/media/images/emoji-sprites/spritesheet-1f3fb.png';
|
||||
import sprite1f3fc2x from '@app/media/images/emoji-sprites/[email protected]';
|
||||
@@ -13,15 +14,25 @@ import sprite1f3ff2x from '@app/media/images/emoji-sprites/spritesheet-1f3ff@2x.
|
||||
import sprite1f3ff1x from '@app/media/images/emoji-sprites/spritesheet-1f3ff.png';
|
||||
import spriteDefault2x from '@app/media/images/emoji-sprites/[email protected]';
|
||||
import spriteDefault1x from '@app/media/images/emoji-sprites/spritesheet-emoji.png';
|
||||
import type {CSSProperties} from 'react';
|
||||
|
||||
export const EMOJI_CLAP = '\u{1F44F}';
|
||||
export const EMOJI_SPRITE_SIZE = 32;
|
||||
const EMOJI_SPRITE_SIZE = 32;
|
||||
export const EMOJI_ROW_HEIGHT = 48;
|
||||
export const EMOJI_PICKER_CUSTOM_EMOJI_SIZE = 48;
|
||||
export const CATEGORY_HEADER_HEIGHT = 32;
|
||||
export const EMOJIS_PER_ROW = 9;
|
||||
export const OVERSCAN_ROWS = 5;
|
||||
|
||||
export const getEmojiSpriteSheetLayout = (index: number, perRow: number, rows: number): CSSProperties => {
|
||||
const column = index % perRow;
|
||||
const row = Math.floor(index / perRow);
|
||||
return {
|
||||
backgroundPosition: `${remFromPx(-column * EMOJI_SPRITE_SIZE)} ${remFromPx(-row * EMOJI_SPRITE_SIZE)}`,
|
||||
backgroundSize: `${remFromPx(perRow * EMOJI_SPRITE_SIZE)} ${remFromPx(rows * EMOJI_SPRITE_SIZE)}`,
|
||||
};
|
||||
};
|
||||
|
||||
interface SpriteSheetOptions {
|
||||
retina?: boolean;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
import {useShouldAnimate} from '@app/features/app/hooks/useShouldAnimate';
|
||||
import styles from '@app/features/channel/components/EmojiPicker.module.css';
|
||||
import {
|
||||
EMOJI_SPRITE_SIZE,
|
||||
getEmojiSpriteSheetLayout,
|
||||
getSpriteSheetBackground,
|
||||
} from '@app/features/channel/components/emoji_picker/EmojiPickerConstants';
|
||||
import Emoji from '@app/features/emoji/state/Emoji';
|
||||
import type {FlatEmoji} from '@app/features/emoji/types/EmojiTypes';
|
||||
import {getEmojiDisplayDataWithSkinTone} from '@app/features/expressions/utils/SkinToneUtils';
|
||||
import {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import UnicodeEmojis, {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import Guilds from '@app/features/guild/state/Guilds';
|
||||
import {isFirefoxBrowser} from '@app/features/ui/utils/NativeUtils';
|
||||
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
|
||||
@@ -46,15 +46,13 @@ export const EmojiPickerInspector = observer(({hoveredEmoji}: EmojiPickerInspect
|
||||
const index = hasDiversity ? emoji.diversityIndex : emoji.index;
|
||||
if (index === undefined) return {url: emoji.url, useImg: true};
|
||||
const perRow = hasDiversity ? EMOJI_SPRITES.DiversityPerRow : EMOJI_SPRITES.NonDiversityPerRow;
|
||||
const x = -(index % perRow) * EMOJI_SPRITE_SIZE;
|
||||
const y = -Math.floor(index / perRow) * EMOJI_SPRITE_SIZE;
|
||||
const rows = Math.ceil(
|
||||
(hasDiversity ? UnicodeEmojis.numDiversitySprites : UnicodeEmojis.numNonDiversitySprites) / perRow,
|
||||
);
|
||||
return {
|
||||
style: {
|
||||
backgroundImage: getSpriteSheetBackground(hasDiversity ? skinTone : ''),
|
||||
backgroundPosition: `${x}px ${y}px`,
|
||||
backgroundSize: hasDiversity
|
||||
? `${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.DiversityPerRow}px`
|
||||
: `${EMOJI_SPRITE_SIZE * EMOJI_SPRITES.NonDiversityPerRow}px`,
|
||||
...getEmojiSpriteSheetLayout(index, perRow, rows),
|
||||
},
|
||||
useImg: false,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import styles from '@app/features/channel/components/EmojiPicker.module.css';
|
||||
import {
|
||||
EMOJI_PICKER_CUSTOM_EMOJI_SIZE,
|
||||
EMOJI_SPRITE_SIZE,
|
||||
getEmojiSpriteSheetLayout,
|
||||
getSpriteSheetBackground,
|
||||
} from '@app/features/channel/components/emoji_picker/EmojiPickerConstants';
|
||||
import type {Channel} from '@app/features/channel/models/Channel';
|
||||
@@ -11,7 +11,7 @@ import * as EmojiPickerCommands from '@app/features/emoji/commands/EmojiPickerCo
|
||||
import type {FlatEmoji} from '@app/features/emoji/types/EmojiTypes';
|
||||
import {checkEmojiAvailability} from '@app/features/expressions/utils/ExpressionPermissionUtils';
|
||||
import {getEmojiDisplayDataWithSkinTone} from '@app/features/expressions/utils/SkinToneUtils';
|
||||
import {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import UnicodeEmojis, {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis';
|
||||
import {setUrlQueryParams} from '@app/features/messaging/utils/MessagingUrlUtils';
|
||||
import {EmojiContextMenuItems} from '@app/features/ui/action_menu/items/EmojiContextMenuItems';
|
||||
import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands';
|
||||
@@ -27,7 +27,6 @@ interface EmojiRendererProps {
|
||||
handleHover: (emoji: FlatEmoji | null) => void;
|
||||
handleSelect: (emoji: FlatEmoji, shiftKey?: boolean) => void;
|
||||
skinTone: string;
|
||||
spriteSheetSizes: {nonDiversitySize: string; diversitySize: string};
|
||||
channel: Channel | null;
|
||||
shouldAnimate: boolean;
|
||||
isHighlighted?: boolean;
|
||||
@@ -41,7 +40,6 @@ export const EmojiRenderer = React.forwardRef<HTMLButtonElement, EmojiRendererPr
|
||||
handleHover,
|
||||
handleSelect,
|
||||
skinTone,
|
||||
spriteSheetSizes,
|
||||
channel,
|
||||
shouldAnimate,
|
||||
isHighlighted = false,
|
||||
@@ -169,12 +167,12 @@ export const EmojiRenderer = React.forwardRef<HTMLButtonElement, EmojiRendererPr
|
||||
);
|
||||
}
|
||||
const perRow = hasDiversity ? EMOJI_SPRITES.DiversityPerRow : EMOJI_SPRITES.NonDiversityPerRow;
|
||||
const x = -(index % perRow) * EMOJI_SPRITE_SIZE;
|
||||
const y = -Math.floor(index / perRow) * EMOJI_SPRITE_SIZE;
|
||||
const rows = Math.ceil(
|
||||
(hasDiversity ? UnicodeEmojis.numDiversitySprites : UnicodeEmojis.numNonDiversitySprites) / perRow,
|
||||
);
|
||||
const spriteStyle = {
|
||||
backgroundImage: getSpriteSheetBackground(hasDiversity ? skinTone : ''),
|
||||
backgroundPosition: `${x}px ${y}px`,
|
||||
backgroundSize: hasDiversity ? spriteSheetSizes.diversitySize : spriteSheetSizes.nonDiversitySize,
|
||||
...getEmojiSpriteSheetLayout(index, perRow, rows),
|
||||
};
|
||||
return renderButton(
|
||||
<div
|
||||
|
||||
@@ -28,7 +28,6 @@ interface VirtualRowRendererProps {
|
||||
handleHover: (emoji: FlatEmoji | null, row?: number, column?: number) => void;
|
||||
handleSelect: (emoji: FlatEmoji, shiftKey?: boolean) => void;
|
||||
skinTone: string;
|
||||
spriteSheetSizes: {nonDiversitySize: string; diversitySize: string};
|
||||
channel: Channel | null;
|
||||
shouldAnimate: boolean;
|
||||
gridColumns?: number;
|
||||
@@ -47,7 +46,6 @@ const VirtualRowRenderer: React.FC<VirtualRowRendererProps> = React.memo(
|
||||
handleHover,
|
||||
handleSelect,
|
||||
skinTone,
|
||||
spriteSheetSizes,
|
||||
channel,
|
||||
shouldAnimate,
|
||||
gridColumns = 9,
|
||||
@@ -175,7 +173,6 @@ const VirtualRowRenderer: React.FC<VirtualRowRendererProps> = React.memo(
|
||||
handleHover={(e) => handleHover(e, emojiRowIndex, colIndex)}
|
||||
handleSelect={handleSelect}
|
||||
skinTone={skinTone}
|
||||
spriteSheetSizes={spriteSheetSizes}
|
||||
channel={channel}
|
||||
shouldAnimate={shouldAnimate}
|
||||
isHighlighted={shouldHighlight}
|
||||
@@ -203,7 +200,6 @@ interface VirtualizedRowProps {
|
||||
handleHover: (emoji: FlatEmoji | null, row?: number, column?: number) => void;
|
||||
handleSelect: (emoji: FlatEmoji, shiftKey?: boolean) => void;
|
||||
skinTone: string;
|
||||
spriteSheetSizes: {nonDiversitySize: string; diversitySize: string};
|
||||
channel: Channel | null;
|
||||
allowAnimation: boolean;
|
||||
gridColumns?: number;
|
||||
@@ -222,7 +218,6 @@ export const VirtualizedRow: React.FC<VirtualizedRowProps> = observer(
|
||||
handleHover,
|
||||
handleSelect,
|
||||
skinTone,
|
||||
spriteSheetSizes,
|
||||
channel,
|
||||
allowAnimation,
|
||||
gridColumns,
|
||||
@@ -287,7 +282,6 @@ export const VirtualizedRow: React.FC<VirtualizedRowProps> = observer(
|
||||
handleHover={handleHover}
|
||||
handleSelect={handleSelect}
|
||||
skinTone={skinTone}
|
||||
spriteSheetSizes={spriteSheetSizes}
|
||||
channel={channel}
|
||||
shouldAnimate={allowAnimation && isInViewport}
|
||||
gridColumns={gridColumns}
|
||||
|
||||
Reference in new issue
Block a user