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

Add Custom Emojis Support #877

Merged
merged 6 commits into from
Mar 27, 2023
Merged

Conversation

makotech222
Copy link
Contributor

@makotech222 makotech222 commented Dec 11, 2022

This PR adds the custom emoji functionality from LemmyNet/lemmy#2419 (comment)

In the frontend, I've added the following:

  • Update post-listing to display a new markdown icon to open emoji picker

image

  • Update markdown-textarea to handle emoji-picker

  • Update app startup to build emoji list from siteres

  • Update Tribute component to include custom emojis
    image

  • Tabify Site Settings page
    image

  • Add Custom Emoji admin tab for add/edit/delete custom emojis. Clicking on emoji in picker will scroll to entry in table.
    image

Deficiencies:

Open Questions:

  • Better icon for 'save' in the table? Currently using 'edit' icon.
  • Is including Image Url column necessary? Allows user to link to external images, but takes up space. I assume most people will just upload images to their server.

@Nutomic
Copy link
Member

Nutomic commented Dec 12, 2022

I think a good solution would be to split the site settings page into different tabs.

@makotech222 makotech222 force-pushed the custom-emojis branch 2 times, most recently from acef14f to 7e08763 Compare December 12, 2022 23:02
@dessalines
Copy link
Member

Question: is "Category" for emoji-mart? So these all wouldn't just go in a single "custom" category, but potentially many custom categories?

If we go this route, I think it would be prudent to add api endpoints for PUT/POST/DELETE of the custom emoji entities, rather than use Site.

Sounds good to me.

Currently, I am just adding a <script> tag to the server's html to load the emoji-mart library. Seeing as how we don't do this for any other library, I imagine we would want to change this.

Fairly sure you can define that custom library location in the package.json and it will download it to node_modules. If that doesn't work, you can reference the git repo, if the js file is hosted there.

@makotech222
Copy link
Contributor Author

Yeah, Category is something for emoji-mart. You can add emojis to custom categories and it will group them.

@makotech222 makotech222 force-pushed the custom-emojis branch 3 times, most recently from 7aac252 to ec86651 Compare December 15, 2022 18:48
@makotech222 makotech222 changed the title [Draft] Add Custom Emojis Support Add Custom Emojis Support Dec 15, 2022
@makotech222 makotech222 marked this pull request as ready for review December 15, 2022 19:11
@makotech222 makotech222 force-pushed the custom-emojis branch 3 times, most recently from 105e39e to 390b44e Compare December 30, 2022 16:23
@makotech222 makotech222 force-pushed the custom-emojis branch 2 times, most recently from 6a25461 to a4212dd Compare January 7, 2023 23:01
@makotech222
Copy link
Contributor Author

Okay with the latest commit, the emoji picker will place a markdown image, with its 'title' attr set to the shortcode. When the markdown renderer encounters an image, it will check if it is a custom emoji, and if so it will add the 'icon-emoji' css class, which limits the size of the image.

@dessalines
Copy link
Member

I'm just getting around to testing this now. There doesn't seem to be a save button for adding the custom emojis.

@makotech222
Copy link
Contributor Author

Its the icon on each row next to the delete button. Its not a great icon, but couldn't find any better suiting ones.



let custom_emojis: any[] = [];
let custom_emojis_lookup: { [k: string]: CustomEmojiView } = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a typescript Map<X, Y> for this, and probably add it to the globals near the top. camelCase too.

src/shared/utils.ts Show resolved Hide resolved
@@ -678,6 +673,104 @@ export function setupTribute() {
});
}



let custom_emojis: any[] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type this strongly, and use camelCase for typescript vars. It should probably be moved to the top too, since its a global.

<button
className={`nav-link btn ${this.state.currentTab == "emojis" && "active"
}`}
onClick={() => { this.handleSwitchTab("emojis"); }}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use linkEvent here. Check settings.tsx for an example.

<li className="nav-item">
<button
className={`nav-link btn ${this.state.currentTab == "site" && "active"}`}
onClick={() => { this.handleSwitchTab("site"); }}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, use linkEvent.

htmlFor={index.toString()}
className="pointer text-muted small font-weight-bold"
>
{cv.image_url.length > 0 && <img style="max-width: 24px; max-height: 24px; display: inline-block;" src={cv.image_url} />}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a style in main.css

disabled={cv.id > 0}
value={cv.shortcode}
onInput={(s: any) =>
this.handleEmojiShortCodeChange(s.target.value, index)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these should use linkEvent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With linkEvent, I can't pass any parameters to the function. I can technically embed it in the html element, but that seems so much messier. I've already done same function calls in earlier PRs without linkEvent. From documentation, it just seems like its slightly faster?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To pass data to linkEvent, do:

onClick = {linkEvent(
  {some_data_here},
  this.handleBleh
)}

<br />
<button
className="btn btn-sm btn-secondary mr-2"
onClick={e => this.handleAddEmojiClick(e)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a button to add a block, but no button to save the emojis to lemmy.

let data = wsJsonToRes<DeleteCustomEmojiResponse>(msg);
if (data.success) {
let custom_emojis = [...this.state!.custom_emojis.filter(x => x.id != data.id)];
this.setState({ custom_emojis });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also be updating the global emoji list / maps.

src/shared/components/home/emojis-form.tsx Show resolved Hide resolved
@dessalines
Copy link
Member

That icon looks like an edit. Once you turn it from a table into a bootstrap form, it'll probably be better to name the button Save for clarity.

@makotech222
Copy link
Contributor Author

That icon looks like an edit. Once you turn it from a table into a bootstrap form, it'll probably be better to name the button Save for clarity.

Is this okay?

image

Id prefer a better icon, as its takes up less width, but I couldn't find any in our icon set that worked properly.

@makotech222 makotech222 force-pushed the custom-emojis branch 2 times, most recently from 00cdbd8 to 000e1f6 Compare February 12, 2023 04:05
@makotech222
Copy link
Contributor Author

Among many fixes today, i added pagination to the admin panel for emojis, to deal with performance issues with large size lists.

src/shared/utils.ts Show resolved Hide resolved
{this.admins()}
{this.bannedUsers()}
</div>
<div >
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird extra space, wonder how this is passing lint.

@@ -179,6 +209,11 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
);
}

handleSwitchTab(i: AdminSettings, event: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the convention for tab switching from settings.tsx, line 214 .

@@ -226,6 +228,7 @@ export class MarkdownTextArea extends Component<
>
<Icon icon="link" classes="icon-inline" />
</button>
<EmojiPicker onEmojiClick={(e) => this.handleEmoji(this,e)}></EmojiPicker>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use linkEvent

page: number;
}

class CustomEmojiViewForm {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a class and not an interface?

>
<thead className="pointer">
<tr>
<th style="width:70px;">{i18n.t("column_emoji")}</th>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but at the very least remove the hard-coded widths ( those aren't going to be correct for any device but the one you coded them from), and use bootstrap table directives like in communities.tsx . As I said though, tables are really terrible for skinny devices like phones, which is why we eventually need to get rid of that, and switch to using responsive design.

Bootstrap has responsive form examples, which is what this is, and we have a good amount of responsive forms in lemmy.

disabled={cv.id > 0}
value={cv.shortcode}
onInput={(s: any) =>
this.handleEmojiShortCodeChange(s.target.value, index)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To pass data to linkEvent, do:

onClick = {linkEvent(
  {some_data_here},
  this.handleBleh
)}

</tr>
</thead>
<tbody>
{this.state.customEmojis.slice((this.state.page - 1) * this.itemsPerPage, ((this.state.page - 1) * this.itemsPerPage) + this.itemsPerPage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave complicated front-end paging logic out of this, if we need to later, we can add it in the back end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding paging: All emojis are already loaded on frontend, no need to pull pages from server. We are paging on admin settings because showing all emojis at once (especially when you get to the thousands) causes page to slow to a crawl and become unusable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're really getting to the level of crashing the front end because there are too many emojis, then we really need to take them out of GetSite into a paged emoji fetcher.

Front end hacks like this have to be replicated on all lemmy front ends, which means we're not structuring things correctly. I'm okay with merging this as is, but please add an issue to the Lemmy repo for breaking emojis into its own paged endpoint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider adding a hardcoded emoji limit in the backend then. If you hit the limit, you just need to remove an existing emoji before adding a new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All users need all of the emojis available; when they open the emoji-picker, we need to know what to put in there all at once, not paged. Because of this, its kind of pointless to also have paged endpoints for emojis. I personally consider front-end paging pretty trivial; its mainly necessary once you get to the number of emojis that sites like hexbear have, which ends up with >2000 elements. Rendering all those at once is unfortunately a weakness of html and will cause page to get unresponsive.

Adding a hard coded limit would be unfortunate, as hexbear would be way over the limit of what is responsive on that page. Seems unnecessary, since the obvious way to work with it is just paginating on the front end. Alternate frontends can go without paginating, as long as they understand that it will get slower as more emojis are added.

this.setState({ page: page });
}

handleEmojiClick = (e: any) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No closures, functions only please.

src/shared/components/home/emojis-form.tsx Show resolved Hide resolved
@makotech222
Copy link
Contributor Author

@dessalines Okay I fixed the comments. I updated the table to be more like communities. I do still have the last column with a set width, because the table doesn't really allow two elements in the same column without wrapping it, causing the row width the expand
desktop:
image
mobile:
image

Copy link
Member

@dessalines dessalines left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the back-end gets merged, I'll test this again, and merge it if everything works correctly.

</tr>
</thead>
<tbody>
{this.state.customEmojis.slice((this.state.page - 1) * this.itemsPerPage, ((this.state.page - 1) * this.itemsPerPage) + this.itemsPerPage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're really getting to the level of crashing the front end because there are too many emojis, then we really need to take them out of GetSite into a paged emoji fetcher.

Front end hacks like this have to be replicated on all lemmy front ends, which means we're not structuring things correctly. I'm okay with merging this as is, but please add an issue to the Lemmy repo for breaking emojis into its own paged endpoint.

@makotech222
Copy link
Contributor Author

@dessalines Hey, any open requests for changes here? Or just need to test?

@dessalines dessalines merged commit 4499579 into LemmyNet:main Mar 27, 2023
@dessalines
Copy link
Member

Okay I deployed the new lemmy-js-client, and merged this fixing a few conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants