Skip to content

Commit 0c6c155

Browse files
authored
Merge pull request #11 from AndrewRyanChama/ar/emojipicker
Add an emoji picker
2 parents 5b96624 + 003ac7e commit 0c6c155

File tree

15 files changed

+234
-84
lines changed

15 files changed

+234
-84
lines changed

res/css/views/emojipicker/_EmojiPicker.scss

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ limitations under the License.
3434
.mx_EmojiPicker_header {
3535
padding: 4px 8px 0;
3636
border-bottom: 1px solid $message-action-bar-border-color;
37+
white-space: nowrap;
38+
overflow: hidden;
3739
}
3840

3941
.mx_EmojiPicker_anchor {
@@ -56,7 +58,7 @@ limitations under the License.
5658
}
5759
}
5860

59-
.mx_EmojiPicker_anchor::before {
61+
.mx_EmojiPicker_anchor:not(.mx_CustomEmojiCategory)::before {
6062
background-color: $primary-content;
6163
content: '';
6264
display: inline-block;
@@ -66,7 +68,7 @@ limitations under the License.
6668
height: 100%;
6769
}
6870

69-
.mx_EmojiPicker_anchor:disabled::before {
71+
.mx_EmojiPicker_anchor:disabled:not(.mx_CustomEmojiCategory)::before {
7072
background-color: $focus-bg-color;
7173
}
7274

@@ -227,3 +229,22 @@ limitations under the License.
227229
.mx_EmojiPicker_quick_header .mx_EmojiPicker_name {
228230
margin-right: 4px;
229231
}
232+
233+
.mx_customEmoji_image {
234+
object-fit: contain;
235+
aspect-ratio: 1;
236+
vertical-align: top;
237+
background-color: $background;
238+
width: 24px;
239+
height: 24px;
240+
}
241+
242+
.mx_CustomEmojiCategory {
243+
vertical-align: top;
244+
245+
img {
246+
display: inline-block;
247+
width: 100%;
248+
height: 100%;
249+
}
250+
}

src/autocomplete/EmojiProvider.tsx

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import SettingsStore from "../settings/SettingsStore";
3333
import { EMOJI, IEmoji } from '../emoji';
3434
import { TimelineRenderingType } from '../contexts/RoomContext';
3535
import { mediaFromMxc } from '../customisations/Media';
36+
import { ICustomEmoji, loadImageSet } from '../emojipicker/customemoji';
3637

3738
const LIMIT = 20;
3839

@@ -45,12 +46,6 @@ interface ISortedEmoji {
4546
_orderBy: number;
4647
}
4748

48-
export interface ICustomEmoji {
49-
shortcodes: string[];
50-
emoticon?: string;
51-
url: string;
52-
}
53-
5449
const SORTED_EMOJI: ISortedEmoji[] = EMOJI.sort((a, b) => {
5550
if (a.group === b.group) {
5651
return a.order - b.order;
@@ -98,11 +93,8 @@ export default class EmojiProvider extends AutocompleteProvider {
9893
});
9994

10095
// Load this room's image sets.
101-
const loadedImages: ICustomEmoji[] = [];
10296
const imageSetEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
103-
imageSetEvents.forEach(imageSetEvent => {
104-
this.loadImageSet(loadedImages, imageSetEvent);
105-
});
97+
const loadedImages: ICustomEmoji[] = imageSetEvents.flatMap(imageSetEvent => loadImageSet(imageSetEvent));
10698
const sortedCustomImages = loadedImages.map((emoji, index) => ({
10799
emoji,
108100
// Include the index so that we can preserve the original order
@@ -115,20 +107,6 @@ export default class EmojiProvider extends AutocompleteProvider {
115107
});
116108
}
117109

118-
private loadImageSet(loadedImages: ICustomEmoji[], imageSetEvent: MatrixEvent): void {
119-
const images = imageSetEvent.getContent().images;
120-
if (!images) {
121-
return;
122-
}
123-
for (const imageKey in images) {
124-
const imageData = images[imageKey];
125-
loadedImages.push({
126-
shortcodes: [imageKey],
127-
url: imageData.url,
128-
});
129-
}
130-
}
131-
132110
async getCompletions(
133111
query: string,
134112
selection: ISelectionRange,
@@ -196,13 +174,9 @@ export default class EmojiProvider extends AutocompleteProvider {
196174
component: (
197175
<PillCompletion title={`:${c.emoji.shortcodes[0]}:`}>
198176
<img
199-
className="mx_BaseAvatar_image"
177+
className="mx_customEmoji_image"
200178
src={mediaUrl}
201-
alt={c.emoji.shortcodes[0]}
202-
style={{
203-
width: '24px',
204-
height: '24px',
205-
}} />
179+
alt={c.emoji.shortcodes[0]} />
206180
</PillCompletion>
207181
),
208182
range,

src/components/views/emojipicker/Category.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ import { CATEGORY_HEADER_HEIGHT, EMOJI_HEIGHT, EMOJIS_PER_ROW } from "./EmojiPic
2121
import LazyRenderList from "../elements/LazyRenderList";
2222
import { DATA_BY_CATEGORY, IEmoji } from "../../../emoji";
2323
import Emoji from './Emoji';
24+
import { ICustomEmoji } from '../../../emojipicker/customemoji';
2425

2526
const OVERFLOW_ROWS = 3;
2627

27-
export type CategoryKey = (keyof typeof DATA_BY_CATEGORY) | "recent";
28+
export type CategoryKey = (keyof typeof DATA_BY_CATEGORY) | "recent" | "room";
2829

2930
export interface ICategory {
3031
id: CategoryKey;
3132
name: string;
33+
representativeEmoji?: ICustomEmoji;
3234
enabled: boolean;
3335
visible: boolean;
3436
ref: RefObject<HTMLButtonElement>;
@@ -37,14 +39,14 @@ export interface ICategory {
3739
interface IProps {
3840
id: string;
3941
name: string;
40-
emojis: IEmoji[];
42+
emojis: Array<IEmoji | ICustomEmoji>;
4143
selectedEmojis: Set<string>;
4244
heightBefore: number;
4345
viewportHeight: number;
4446
scrollTop: number;
45-
onClick(emoji: IEmoji): void;
46-
onMouseEnter(emoji: IEmoji): void;
47-
onMouseLeave(emoji: IEmoji): void;
47+
onClick(emoji: IEmoji | ICustomEmoji): void;
48+
onMouseEnter(emoji: IEmoji | ICustomEmoji): void;
49+
onMouseLeave(emoji: IEmoji | ICustomEmoji): void;
4850
}
4951

5052
class Category extends React.PureComponent<IProps> {
@@ -54,7 +56,7 @@ class Category extends React.PureComponent<IProps> {
5456
return (<div key={rowIndex}>{
5557
emojisForRow.map(emoji => (
5658
<Emoji
57-
key={emoji.hexcode}
59+
key={'hexcode' in emoji ? emoji.hexcode : emoji.shortcodes[0]}
5860
emoji={emoji}
5961
selectedEmojis={selectedEmojis}
6062
onClick={onClick}

src/components/views/emojipicker/Emoji.tsx

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,48 @@ import React from 'react';
1919

2020
import { MenuItem } from "../../structures/ContextMenu";
2121
import { IEmoji } from "../../../emoji";
22+
import { ICustomEmoji } from '../../../emojipicker/customemoji';
23+
import { mediaFromMxc } from '../../../customisations/Media';
2224

2325
interface IProps {
24-
emoji: IEmoji;
26+
emoji: IEmoji | ICustomEmoji;
2527
selectedEmojis?: Set<string>;
26-
onClick(emoji: IEmoji): void;
27-
onMouseEnter(emoji: IEmoji): void;
28-
onMouseLeave(emoji: IEmoji): void;
28+
onClick(emoji: IEmoji | ICustomEmoji): void;
29+
onMouseEnter(emoji: IEmoji | ICustomEmoji): void;
30+
onMouseLeave(emoji: IEmoji | ICustomEmoji): void;
2931
}
3032

3133
class Emoji extends React.PureComponent<IProps> {
3234
render() {
3335
const { onClick, onMouseEnter, onMouseLeave, emoji, selectedEmojis } = this.props;
34-
const isSelected = selectedEmojis && selectedEmojis.has(emoji.unicode);
36+
37+
let emojiElement: JSX.Element;
38+
if ('unicode' in emoji) {
39+
const isSelected = selectedEmojis && selectedEmojis.has(emoji.unicode);
40+
emojiElement = <div className={`mx_EmojiPicker_item ${isSelected ? 'mx_EmojiPicker_item_selected' : ''}`}>
41+
{emoji.unicode}
42+
</div>;
43+
} else {
44+
const mediaUrl = mediaFromMxc(emoji.url).getThumbnailOfSourceHttp(24, 24, 'scale');
45+
emojiElement = <div className="mx_EmojiPicker_item">
46+
<img
47+
className="mx_customEmoji_image"
48+
src={mediaUrl}
49+
alt={emoji.shortcodes[0]} />
50+
</div>
51+
}
52+
emojiElement;
53+
3554
return (
3655
<MenuItem
3756
element="li"
3857
onClick={() => onClick(emoji)}
3958
onMouseEnter={() => onMouseEnter(emoji)}
4059
onMouseLeave={() => onMouseLeave(emoji)}
4160
className="mx_EmojiPicker_item_wrapper"
42-
label={emoji.unicode}
61+
label={'unicode' in emoji ? emoji.unicode : emoji.shortcodes[0]}
4362
>
44-
<div className={`mx_EmojiPicker_item ${isSelected ? 'mx_EmojiPicker_item_selected' : ''}`}>
45-
{ emoji.unicode }
46-
</div>
63+
{emojiElement}
4764
</MenuItem>
4865
);
4966
}

0 commit comments

Comments
 (0)