Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: replace reaction tray and directly open the emoji picker #2537

Merged
merged 1 commit into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/message-input/emoji-picker/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@
.emoji-mart-dark {
@include glass-text-primary-color;
border-color: #000;
background-color: #0a0a0abf;
background-color: rgba(10, 10, 10, 1);
}

.emoji-mart-dark .emoji-mart-bar {
Expand All @@ -505,7 +505,7 @@
@include glass-text-primary-color;

border-color: #000;
background-color: #0a0a0abf;
background-color: rgba(10, 10, 10, 1);
}

.emoji-mart-dark .emoji-mart-search-icon svg {
Expand All @@ -519,7 +519,7 @@
.emoji-mart-dark .emoji-mart-category-label span {
@include glass-text-primary-color;

background-color: #0a0a0abf;
background-color: rgba(10, 10, 10, 1);
}

.emoji-mart-dark .emoji-mart-skin-swatches {
Expand Down
1 change: 1 addition & 0 deletions src/components/message/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ export class Message extends React.Component<Properties, State> {
const reactionProps = {
onOpenChange: this.handleOpenReactionMenu,
onSelectReaction: this.onEmojiReaction,
isOwner: this.props.isOwner,
};

const onClose = () => {
Expand Down
145 changes: 10 additions & 135 deletions src/components/reaction-menu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,30 @@
import React, { createRef } from 'react';
import { createPortal } from 'react-dom';
import { Emoji, Picker } from 'emoji-mart';
import { Picker } from 'emoji-mart';

import { IconButton } from '@zero-tech/zui/components';
import { IconDotsHorizontal, IconHeart } from '@zero-tech/zui/icons';
import { IconHeart } from '@zero-tech/zui/icons';
import { ViewModes } from '../../shared-components/theme-engine';

import './styles.scss';
import classNames from 'classnames';

export interface Properties {
isOwner: boolean;
onOpenChange?: (isOpen: boolean) => void;
onSelectReaction: (emoji) => void;
}

export interface State {
isReactionTrayOpen: boolean;
isEmojiPickerOpen: boolean;
isProcessing: boolean;
}

const commonEmojiMapping = {
thumbsup: '👍',
heart: '❤️',
joy: '😂',
cry: '😢',
astonished: '😲',
};

export class ReactionMenu extends React.Component<Properties, State> {
ref = createRef<HTMLDivElement>();
triggerRef = createRef<HTMLDivElement>();
emojiPickerRef = createRef<HTMLDivElement>();

state = {
isReactionTrayOpen: false,
isEmojiPickerOpen: false,
isProcessing: false,
};
Expand All @@ -51,14 +42,12 @@ export class ReactionMenu extends React.Component<Properties, State> {

if (
!(
(this.ref.current && this.ref.current.contains(event.target)) ||
(this.triggerRef.current && this.triggerRef.current.contains(event.target)) ||
(this.emojiPickerRef.current && this.emojiPickerRef.current.contains(event.target))
)
) {
this.setState(
{
isReactionTrayOpen: false,
isEmojiPickerOpen: false,
isProcessing: false,
},
Expand All @@ -69,126 +58,18 @@ export class ReactionMenu extends React.Component<Properties, State> {
}
};

onSelect = (emojiId) => {
console.log('Reaction clicked:', emojiId);
if (this.state.isProcessing) {
console.log('Processing state prevented click');
return;
}

this.setState({ isProcessing: true }, async () => {
console.log('Starting reaction processing');
try {
const emojiCharacter = commonEmojiMapping[emojiId];
if (emojiCharacter) {
console.log('Sending reaction:', emojiCharacter);
await this.props.onSelectReaction(emojiCharacter);
console.log('Reaction sent successfully');
this.setState(
{
isReactionTrayOpen: false,
isEmojiPickerOpen: false,
},
() => {
this.props.onOpenChange?.(false);
}
);
}
} catch (error) {
console.error('Error processing reaction:', error);
} finally {
console.log('Finishing reaction processing');
this.setState({ isProcessing: false });
}
});
};

toggleReactionTray = () => {
this.setState((prevState) => {
const isReactionTrayOpen = !prevState.isReactionTrayOpen;
this.props.onOpenChange?.(isReactionTrayOpen);
return { isReactionTrayOpen, isEmojiPickerOpen: false };
});
};

toggleEmojiPicker = () => {
this.setState((prevState) => ({
isEmojiPickerOpen: !prevState.isEmojiPickerOpen,
isReactionTrayOpen: false,
}));
};

renderCommonReactions() {
const commonReactions = [
'thumbsup',
'heart',
'joy',
'cry',
'astonished',
];

return commonReactions.map((emojiId) => (
<span
className='reaction-tray-item'
key={emojiId}
onClick={(e) => {
e.stopPropagation(); // Stop event from reaching the underlay
this.onSelect(emojiId);
}}
>
<Emoji emoji={emojiId} size={20} />
</span>
));
}

renderReactionTray() {
if (!this.triggerRef.current) {
return null;
}

const triggerRect = this.triggerRef.current.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const trayHeight = 56;
const trayWidth = 256;

const shouldRenderAbove = triggerRect.bottom + trayHeight > viewportHeight;

const trayStyles = {
top: shouldRenderAbove
? `${triggerRect.top + window.scrollY - trayHeight - 8}px`
: `${triggerRect.bottom + window.scrollY + 8}px`,
left: `${triggerRect.left + window.scrollX + triggerRect.width / 2 - trayWidth / 2}px`,
transformOrigin: shouldRenderAbove ? 'bottom center' : 'top center',
transform: 'translateY(0)',
};

return createPortal(
<>
<div className='reaction-menu__underlay' onClick={this.toggleReactionTray}></div>

<div className='reaction-tray' style={trayStyles} ref={this.ref}>
{this.renderCommonReactions()}

<div className='reaction-tray-item'>
<IconButton
className='emoji-picker-trigger-icon'
Icon={IconDotsHorizontal}
size={20}
onClick={(e) => {
e.stopPropagation();
this.toggleEmojiPicker();
}}
/>
</div>
</div>
</>,
document.body
);
}

renderEmojiPicker() {
return createPortal(
<div className='emoji-picker' ref={this.emojiPickerRef}>
<div
className={classNames('emoji-picker', this.props.isOwner && 'emoji-picker--owner')}
ref={this.emojiPickerRef}
>
<Picker
emoji='mechanical_arm'
title='ZOS'
Expand All @@ -208,20 +89,14 @@ export class ReactionMenu extends React.Component<Properties, State> {
};

render() {
const { isReactionTrayOpen, isEmojiPickerOpen } = this.state;
const { isEmojiPickerOpen } = this.state;

return (
<div className='reaction-menu'>
<div ref={this.triggerRef}>
<IconButton
className={'reaction-menu-trigger'}
Icon={IconHeart}
size={32}
onClick={this.toggleReactionTray}
/>
<IconButton className={'reaction-menu-trigger'} Icon={IconHeart} size={32} onClick={this.toggleEmojiPicker} />
</div>

{isReactionTrayOpen && this.renderReactionTray()}
{isEmojiPickerOpen && this.renderEmojiPicker()}
</div>
);
Expand Down
54 changes: 4 additions & 50 deletions src/components/reaction-menu/styles.scss
Original file line number Diff line number Diff line change
@@ -1,66 +1,20 @@
@use '~@zero-tech/zui/styles/theme' as theme;
@import '../../glass';

.reaction-tray {
@include flat-thick;
@include glass-shadow-and-blur;

display: flex;
flex-direction: row;
align-items: center;
padding: 4px;
border-radius: 8px;
min-width: 128px;
max-width: 256px;
position: fixed;
z-index: 1002;
}

.reaction-tray-item {
@include glass-text-primary-color;

display: flex;
align-items: center;
outline: none;
border-radius: 8px;
user-select: none;
cursor: pointer;
padding: 8px;

&:hover {
@include glass-state-hover-color;
}

&:active {
background: rgba(163, 162, 163, 0.1);
}
}

.reaction-menu__underlay {
z-index: 1000;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: block;
pointer-events: auto;
background: transparent;
}

.reaction-menu-trigger {
z-index: 1003;
outline: none;
}

.emoji-picker {
position: absolute;
right: 65px;
bottom: 75px;
left: 390px;
z-index: 1010;
color: var(--color-greyscale-11);
}

.emoji-picker-trigger-icon {
background: theme.$color-greyscale-transparency-3;
.emoji-picker--owner {
right: 65px;
left: auto;
}
Loading