mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:42:24 -05:00 
			
		
		
		
	[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…
	
	Add table
		Add a link
		
	
		Reference in a new issue