gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

category-select.jsx (3004B)


      1 /*
      2 	GoToSocial
      3 	Copyright (C) GoToSocial Authors admin@gotosocial.org
      4 	SPDX-License-Identifier: AGPL-3.0-or-later
      5 
      6 	This program is free software: you can redistribute it and/or modify
      7 	it under the terms of the GNU Affero General Public License as published by
      8 	the Free Software Foundation, either version 3 of the License, or
      9 	(at your option) any later version.
     10 
     11 	This program is distributed in the hope that it will be useful,
     12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 	GNU Affero General Public License for more details.
     15 
     16 	You should have received a copy of the GNU Affero General Public License
     17 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18 */
     19 
     20 "use strict";
     21 
     22 const React = require("react");
     23 const splitFilterN = require("split-filter-n");
     24 const syncpipe = require('syncpipe');
     25 const { matchSorter } = require("match-sorter");
     26 
     27 const query = require("../../lib/query");
     28 
     29 const ComboBox = require("../../components/combo-box");
     30 
     31 function useEmojiByCategory(emoji) {
     32 	// split all emoji over an object keyed by the category names (or Unsorted)
     33 	return React.useMemo(() => splitFilterN(
     34 		emoji,
     35 		[],
     36 		(entry) => entry.category ?? "Unsorted"
     37 	), [emoji]);
     38 }
     39 
     40 function CategorySelect({ field, children }) {
     41 	const { value, setIsNew } = field;
     42 
     43 	const {
     44 		data: emoji = [],
     45 		isLoading,
     46 		isSuccess,
     47 		error
     48 	} = query.useListEmojiQuery({ filter: "domain:local" });
     49 
     50 	const emojiByCategory = useEmojiByCategory(emoji);
     51 
     52 	const categories = React.useMemo(() => new Set(Object.keys(emojiByCategory)), [emojiByCategory]);
     53 
     54 	// data used by the ComboBox element to select an emoji category
     55 	const categoryItems = React.useMemo(() => {
     56 		return syncpipe(emojiByCategory, [
     57 			(_) => Object.keys(_),            // just emoji category names
     58 			(_) => matchSorter(_, value, { threshold: matchSorter.rankings.NO_MATCH }),  // sorted by complex algorithm
     59 			(_) => _.map((categoryName) => [  // map to input value, and selectable element with icon
     60 				categoryName,
     61 				<>
     62 					<img src={emojiByCategory[categoryName][0].static_url} aria-hidden="true"></img>
     63 					{categoryName}
     64 				</>
     65 			])
     66 		]);
     67 	}, [emojiByCategory, value]);
     68 
     69 	React.useEffect(() => {
     70 		if (value != undefined && isSuccess && value.trim().length > 0) {
     71 			setIsNew(!categories.has(value.trim()));
     72 		}
     73 	}, [categories, value, isSuccess, setIsNew]);
     74 
     75 	if (error) { // fall back to plain text input, but this would almost certainly have caused a bigger error message elsewhere
     76 		return (
     77 			<>
     78 				<input type="text" placeholder="e.g., reactions" onChange={(e) => { field.value = e.target.value; }} />;
     79 			</>
     80 		);
     81 	} else if (isLoading) {
     82 		return <input type="text" value="Loading categories..." disabled={true} />;
     83 	}
     84 
     85 	return (
     86 		<ComboBox
     87 			field={field}
     88 			items={categoryItems}
     89 			label="Category"
     90 			placeholder="e.g., reactions"
     91 			children={children}
     92 		/>
     93 	);
     94 }
     95 
     96 module.exports = {
     97 	useEmojiByCategory,
     98 	CategorySelect
     99 };