Skip to content

Commit

Permalink
[feature/frontend] filterable local emoji list (#1385)
Browse files Browse the repository at this point in the history
  • Loading branch information
f0x52 committed Jan 27, 2023
1 parent 782169d commit 08f8fea
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 25 deletions.
16 changes: 11 additions & 5 deletions web/source/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -419,11 +419,6 @@ label {
display: flex;
flex-direction: column;

&.scrolling {
max-height: 40rem;
overflow: auto;
}

.header, .entry {
padding: 0.5rem;
}
Expand All @@ -435,6 +430,17 @@ label {
font-weight: bold;
}

.entries {
display: flex;
flex-direction: column;

&.scrolling {
height: 20rem;
max-height: 20rem;
overflow: auto;
}
}

input[type=checkbox] {
margin-left: 0.5rem;
}
Expand Down
62 changes: 57 additions & 5 deletions web/source/settings/admin/emoji/local/overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@

const React = require("react");
const { Link } = require("wouter");
const syncpipe = require("syncpipe");
const { matchSorter } = require("match-sorter");

const NewEmojiForm = require("./new-emoji");
const { useTextInput } = require("../../../lib/form");

const query = require("../../../lib/query");
const { useEmojiByCategory } = require("../category-select");

const Loading = require("../../../components/loading");
const { Error } = require("../../../components/error");
const { TextInput } = require("../../../components/form/inputs");

module.exports = function EmojiOverview({ baseUrl }) {
const {
Expand All @@ -53,23 +58,70 @@ module.exports = function EmojiOverview({ baseUrl }) {

return (
<>
<h1>Custom Emoji (local)</h1>
<h1>Local Custom Emoji</h1>
<p>
To use custom emoji in your toots they have to be 'local' to the instance.
You can either upload them here directly, or copy from those already
present on other (known) instances through the <Link to={`../remote`}>Remote Emoji</Link> page.
</p>
{content}
</>
);
};

function EmojiList({ emoji, baseUrl }) {
const filterField = useTextInput("filter");
const filter = filterField.value;

const emojiByCategory = useEmojiByCategory(emoji);

/* Filter emoji based on shortcode match with user input, hiding empty categories */
const { filteredEmoji, hidden } = React.useMemo(() => {
let hidden = emoji.length;
const filteredEmoji = syncpipe(emojiByCategory, [
(_) => Object.entries(emojiByCategory),
(_) => _.map(([category, entries]) => {
let filteredEntries = matchSorter(entries, filter, { keys: ["shortcode"] });
if (filteredEntries.length == 0) {
return null;
} else {
hidden -= filteredEntries.length;
return [category, filteredEntries];
}
}),
(_) => _.filter((value) => value !== null)
]);

return { filteredEmoji, hidden };
}, [filter, emojiByCategory, emoji.length]);

return (
<div>
<h2>Overview</h2>
{emoji.length > 0
? <span>{emoji.length} custom emoji {hidden > 0 && `(${hidden} filtered)`}</span>
: <span>No custom emoji yet, you can add one below.</span>
}
<div className="list emoji-list">
{emoji.length == 0 && "No local emoji yet, add one below"}
{Object.entries(emojiByCategory).map(([category, entries]) => {
return <EmojiCategory key={category} category={category} entries={entries} baseUrl={baseUrl} />;
})}
<div className="header">
<TextInput
field={filterField}
name="emoji-shortcode"
placeholder="Search"
/>
</div>
<div className="entries scrolling">
{filteredEmoji.length > 0
? (
<div className="entries scrolling">
{filteredEmoji.map(([category, entries]) => {
return <EmojiCategory key={category} category={category} entries={entries} baseUrl={baseUrl} />;
})}
</div>
)
: <div className="entry">No local emoji matched your filter.</div>
}
</div>
</div>
</div>
);
Expand Down
32 changes: 17 additions & 15 deletions web/source/settings/admin/federation/overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,23 @@ module.exports = function InstanceOverview({ baseUrl }) {
<span>
{blockedInstancesList.length} blocked instance{blockedInstancesList.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`}
</span>
<div className="list scrolling">
{filteredInstances.map((entry) => {
return (
<Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}>
<a className="entry nounderline">
<span id="domain">
{entry.domain}
</span>
<span id="date">
{new Date(entry.created_at).toLocaleString()}
</span>
</a>
</Link>
);
})}
<div className="list">
<div className="entries scrolling">
{filteredInstances.map((entry) => {
return (
<Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}>
<a className="entry nounderline">
<span id="domain">
{entry.domain}
</span>
<span id="date">
{new Date(entry.created_at).toLocaleString()}
</span>
</a>
</Link>
);
})}
</div>
</div>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions web/source/settings/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ span.form-info {
.emoji-list {
background: $list-entry-bg;

.header .form-field {
flex: 1 1 auto;
}

.entry {
flex-direction: column;

Expand Down

0 comments on commit 08f8fea

Please sign in to comment.