forked from mirrors/gotosocial
[feature/frontend] filterable local emoji list (#1385)
This commit is contained in:
parent
782169da76
commit
08f8feaec5
4 changed files with 89 additions and 25 deletions
|
@ -419,11 +419,6 @@ label {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.scrolling {
|
||||
max-height: 40rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.header, .entry {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -53,24 +58,71 @@ 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]) => {
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -76,7 +76,8 @@ 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">
|
||||
<div className="list">
|
||||
<div className="entries scrolling">
|
||||
{filteredInstances.map((entry) => {
|
||||
return (
|
||||
<Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}>
|
||||
|
@ -94,6 +95,7 @@ module.exports = function InstanceOverview({ baseUrl }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Link to={`${baseUrl}/import-export`}><a>Or use the bulk import/export interface</a></Link>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -405,6 +405,10 @@ span.form-info {
|
|||
.emoji-list {
|
||||
background: $list-entry-bg;
|
||||
|
||||
.header .form-field {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.entry {
|
||||
flex-direction: column;
|
||||
|
||||
|
|
Loading…
Reference in a new issue