Skip to content

Commit

Permalink
Add Custom Emoji Support
Browse files Browse the repository at this point in the history
  • Loading branch information
makotech222 committed Feb 26, 2023
1 parent 578709b commit 1f15ea5
Show file tree
Hide file tree
Showing 11 changed files with 877 additions and 52 deletions.
2 changes: 1 addition & 1 deletion lemmy-translations
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@babel/preset-env": "7.20.2",
"@babel/preset-typescript": "^7.21.0",
"@babel/runtime": "^7.21.0",
"@emoji-mart/data": "^1.1.0",
"autosize": "^6.0.1",
"babel-loader": "^9.1.2",
"babel-plugin-inferno": "^6.6.0",
Expand All @@ -32,6 +33,7 @@
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"emoji-mart": "^5.4.0",
"emoji-short-name": "^2.0.0",
"express": "~4.18.2",
"html-to-text": "^9.0.4",
Expand All @@ -48,6 +50,7 @@
"lemmy-js-client": "0.17.2-rc.1",
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"markdown-it-emoji": "^2.0.2",
"markdown-it-footnote": "^3.0.3",
"markdown-it-html5-embed": "^1.0.0",
"markdown-it-sub": "^1.0.0",
Expand Down
58 changes: 56 additions & 2 deletions src/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,19 @@
.md-div h1 {
font-size: 2rem;
}

.md-div h2 {
font-size: 1.8rem;
}

.md-div h3 {
font-size: 1.6rem;
}

.md-div h4 {
font-size: 1.4rem;
}

.md-div h5 {
font-size: 1.2rem;
}
Expand All @@ -95,7 +99,7 @@
border-bottom: 2px solid var(--dark);
}

.md-div table tbody + tbody {
.md-div table tbody+tbody {
border-top: 2px solid var(--dark);
}

Expand Down Expand Up @@ -129,10 +133,52 @@
user-select: none;
}

.icon-emoji {
width: 4em;
height: auto;
max-height: inherit;
}

.icon-emoji-admin {
max-width: 24px;
max-height: 24px;
display: inline-block;
}

.icon-inline {
margin-bottom: 2px;
}

.emoji-picker-container {
position: absolute;
top: 30px;
z-index: 1000;
transform: translateX(-50%);
}

@media only screen and (max-width: 992px) {
.emoji-picker-container {
width: 100vw;
transform: translateX(0%);
position: fixed;
left: 0;
}

.emoji-picker-container>section {
width: 100% !important;
}
}

.click-away-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .3);
z-index: 999;
}

.spinner-large {
display: grid;
display: block;
Expand All @@ -149,6 +195,7 @@
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(359deg);
}
Expand Down Expand Up @@ -340,19 +387,24 @@ br.big {
list-style: none;
background: var(--light);
}

.tribute-container li {
padding: 5px 5px;
cursor: pointer;
}

.tribute-container li.highlight {
background: var(--primary);
}

.tribute-container li span {
font-weight: bold;
}

.tribute-container li.no-match {
cursor: default;
}

.tribute-container .menu-highlighted {
font-weight: bold;
}
Expand All @@ -376,11 +428,13 @@ br.big {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}

.lang-select-action {
width: 100px;
}

.lang-select-action:focus {
width: auto;
}
em-emoji-picker{
width:100%;
}
33 changes: 33 additions & 0 deletions src/shared/components/common/emoji-mart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Component } from "inferno";
import { getEmojiMart } from "../../utils";


interface EmojiMartProps {
onEmojiClick?(val: any): any;
pickerOptions: any;
}

export class EmojiMart extends Component<
EmojiMartProps
> {
constructor(props: any, context: any) {
super(props, context);
this.handleEmojiClick = this.handleEmojiClick.bind(this);
}
componentDidMount() {
let div: any = document.getElementById("emoji-picker");
if (div) {
div.appendChild(getEmojiMart(this.handleEmojiClick, this.props.pickerOptions));
}
}

render() {
return (
<div id="emoji-picker"></div>
);
}

handleEmojiClick(e: any) {
this.props.onEmojiClick?.(e);
}
}
62 changes: 62 additions & 0 deletions src/shared/components/common/emoji-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Component, linkEvent } from "inferno";
import { i18n } from "../../i18next";
import { EmojiMart } from "./emoji-mart";
import { Icon } from "./icon";

interface EmojiPickerProps {
onEmojiClick?(val: any): any;
}

interface EmojiPickerState {
showPicker: boolean,
}

export class EmojiPicker extends Component<
EmojiPickerProps,
EmojiPickerState
> {
private emptyState: EmojiPickerState = {
showPicker: false,
};
state: EmojiPickerState;
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
this.handleEmojiClick = this.handleEmojiClick.bind(this);
}
render() {
return (
<span>
<button
className="btn btn-sm text-muted"
data-tippy-content={i18n.t("emoji")}
aria-label={i18n.t("emoji")}
onClick={linkEvent(this,this.togglePicker)}
>
<Icon icon="smile" classes="icon-inline" />
</button>

{this.state.showPicker && (
<>
<div className="emoji-picker-container">
<EmojiMart onEmojiClick={this.handleEmojiClick} pickerOptions={({})}></EmojiMart>
</div>
<div
onClick={linkEvent(this,this.togglePicker)}
className="click-away-container"
/>
</>
)}
</span>
);
}

togglePicker(i: EmojiPicker, e: any) {
e.preventDefault();
i.setState({ showPicker: !i.state.showPicker });
}

handleEmojiClick(e: any) {
this.props.onEmojiClick?.(e);
}
}
19 changes: 19 additions & 0 deletions src/shared/components/common/markdown-textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { pictrsUri } from "../../env";
import { i18n } from "../../i18next";
import { UserService } from "../../services";
import {
customEmojisLookup,
isBrowser,
markdownFieldCharacterLimit,
markdownHelpUrl,
Expand All @@ -17,6 +18,7 @@ import {
setupTribute,
toast,
} from "../../utils";
import { EmojiPicker } from "./emoji-picker";
import { Icon, Spinner } from "./icon";
import { LanguageSelect } from "./language-select";

Expand Down Expand Up @@ -226,6 +228,7 @@ export class MarkdownTextArea extends Component<
>
<Icon icon="link" classes="icon-inline" />
</button>
<EmojiPicker onEmojiClick={(e) => this.handleEmoji(this,e)}></EmojiPicker>
<form className="btn btn-sm text-muted font-weight-bold">
<label
htmlFor={`file-upload-${this.id}`}
Expand Down Expand Up @@ -328,6 +331,22 @@ export class MarkdownTextArea extends Component<
);
}

handleEmoji(i: MarkdownTextArea, e: any) {
let value = e.native;
if (value == null){
let emoji = customEmojisLookup.get(e.id)?.custom_emoji;
if (emoji){
value = `![${emoji.alt_text}](${emoji.image_url} "${emoji.shortcode}")`;
}
}
i.setState({
content: `${i.state.content ?? ""} ${value} `,
});
i.contentChange();
let textarea: any = document.getElementById(i.id);
autosize.update(textarea);
}

handleImageUploadPaste(i: MarkdownTextArea, event: any) {
let image = event.clipboardData.files[0];
if (image) {
Expand Down
Loading

0 comments on commit 1f15ea5

Please sign in to comment.