1
0
Philipp Heckel 3 жил өмнө
parent
commit
4eba641ec3

+ 85 - 8
web/src/components/EmojiPicker.js

@@ -1,8 +1,13 @@
 import * as React from 'react';
+import {useRef, useState} from 'react';
 import Popover from '@mui/material/Popover';
 import Typography from '@mui/material/Typography';
 import {rawEmojis} from '../app/emojis';
 import Box from "@mui/material/Box";
+import TextField from "@mui/material/TextField";
+import {InputAdornment} from "@mui/material";
+import IconButton from "@mui/material/IconButton";
+import {Close} from "@mui/icons-material";
 
 const emojisByCategory = {};
 rawEmojis.forEach(emoji => {
@@ -14,22 +19,69 @@ rawEmojis.forEach(emoji => {
 
 const EmojiPicker = (props) => {
     const open = Boolean(props.anchorEl);
+    const [search, setSearch] = useState("");
+    const searchRef = useRef(null);
+
+    /*
+        FIXME Search is inefficient, somehow make it faster
+
+        useEffect(() => {
+            const matching = rawEmojis.filter(e => {
+                const searchLower = search.toLowerCase();
+                return e.description.toLowerCase().indexOf(searchLower) !== -1
+                    || matchInArray(e.aliases, searchLower)
+                    || matchInArray(e.tags, searchLower);
+            });
+            console.log("matching", matching.length);
+        }, [search]);
+    */
+    const handleSearchClear = () => {
+        setSearch("");
+        searchRef.current?.focus();
+    };
 
     return (
         <>
             <Popover
                 open={open}
-                anchorEl={props.anchorEl}
+                elevation={3}
                 onClose={props.onClose}
+                anchorEl={props.anchorEl}
                 anchorOrigin={{
                     vertical: 'bottom',
                     horizontal: 'left',
                 }}
             >
                 <Box sx={{ padding: 2, paddingRight: 0, width: "370px", maxHeight: "300px" }}>
-                    {Object.keys(emojisByCategory).map(category =>
-                        <Category title={category} emojis={emojisByCategory[category]} onPick={props.onEmojiPick}/>
-                    )}
+                    <TextField
+                        inputRef={searchRef}
+                        margin="dense"
+                        size="small"
+                        placeholder="Search emoji"
+                        value={search}
+                        onChange={ev => setSearch(ev.target.value)}
+                        type="text"
+                        variant="standard"
+                        fullWidth
+                        sx={{ marginTop: 0, paddingRight: 2 }}
+                        InputProps={{
+                            endAdornment:
+                                <InputAdornment position="end" sx={{ display: (search) ? '' : 'none' }}>
+                                    <IconButton size="small" onClick={handleSearchClear} edge="end"><Close/></IconButton>
+                                </InputAdornment>
+                        }}
+                    />
+                    <Box sx={{ display: "flex", flexWrap: "wrap", paddingRight: 0, marginTop: 1 }}>
+                        {Object.keys(emojisByCategory).map(category =>
+                            <Category
+                                key={category}
+                                title={category}
+                                emojis={emojisByCategory[category]}
+                                search={search.toLowerCase()}
+                                onPick={props.onEmojiPick}
+                            />
+                        )}
+                    </Box>
                 </Box>
             </Popover>
         </>
@@ -37,18 +89,36 @@ const EmojiPicker = (props) => {
 };
 
 const Category = (props) => {
+    const showTitle = !props.search;
     return (
         <>
-            <Typography variant="body2">{props.title}</Typography>
-            <Box sx={{ display: "flex", flexWrap: "wrap", paddingRight: 0, marginBottom: 1 }}>
-                {props.emojis.map(emoji => <Emoji emoji={emoji} onClick={() => props.onPick(emoji.aliases[0])}/>)}
-            </Box>
+            {showTitle &&
+                <Typography variant="body1" sx={{ width: "100%", marginTop: 1, marginBottom: 1 }}>
+                    {props.title}
+                </Typography>
+            }
+            {props.emojis.map(emoji =>
+                <Emoji
+                    key={emoji.aliases[0]}
+                    emoji={emoji}
+                    search={props.search}
+                    onClick={() => props.onPick(emoji.aliases[0])}
+                />
+            )}
         </>
     );
 };
 
 const Emoji = (props) => {
     const emoji = props.emoji;
+    const search = props.search;
+    const matches = search === ""
+        || emoji.description.toLowerCase().indexOf(search) !== -1
+        || matchInArray(emoji.aliases, search)
+        || matchInArray(emoji.tags, search);
+    if (!matches) {
+        return null;
+    }
     return (
         <div
             onClick={props.onClick}
@@ -69,4 +139,11 @@ const Emoji = (props) => {
     );
 };
 
+const matchInArray = (arr, search) => {
+    if (!arr || !search) {
+        return false;
+    }
+    return arr.filter(s => s.indexOf(search) !== -1).length > 0;
+}
+
 export default EmojiPicker;

+ 4 - 3
web/src/components/SendDialog.js

@@ -213,11 +213,11 @@ const SendDialog = (props) => {
                 onDragLeave={handleAttachFileDragLeave}/>
             }
             <Dialog maxWidth="md" open={open} onClose={props.onCancel} fullScreen={fullScreen}>
-                <DialogTitle>Publish to {shortUrl(topicUrl)}</DialogTitle>
+                <DialogTitle>{topicUrl ? `Publish to ${shortUrl(topicUrl)}` : "Publish message"}</DialogTitle>
                 <DialogContent>
                     {dropZone && <DropBox/>}
                     {showTopicUrl &&
-                        <ClosableRow disabled={disabled} onClose={() => {
+                        <ClosableRow closable={!!props.topicUrl} disabled={disabled} onClose={() => {
                             setTopicUrl(props.topicUrl);
                             setShowTopicUrl(false);
                         }}>
@@ -468,10 +468,11 @@ const Row = (props) => {
 };
 
 const ClosableRow = (props) => {
+    const closable = (props.hasOwnProperty("closable")) ? props.closable : true;
     return (
         <Row>
             {props.children}
-            <DialogIconButton disabled={props.disabled} onClick={props.onClose} sx={{marginLeft: "6px"}}><Close/></DialogIconButton>
+            {closable && <DialogIconButton disabled={props.disabled} onClick={props.onClose} sx={{marginLeft: "6px"}}><Close/></DialogIconButton>}
         </Row>
     );
 };

+ 0 - 2
web/src/components/SubscribeDialog.js

@@ -3,7 +3,6 @@ import {useState} from 'react';
 import Button from '@mui/material/Button';
 import TextField from '@mui/material/TextField';
 import Dialog from '@mui/material/Dialog';
-import DialogActions from '@mui/material/DialogActions';
 import DialogContent from '@mui/material/DialogContent';
 import DialogContentText from '@mui/material/DialogContentText';
 import DialogTitle from '@mui/material/DialogTitle';
@@ -11,7 +10,6 @@ import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/mate
 import theme from "./theme";
 import api from "../app/Api";
 import {topicUrl, validTopic, validUrl} from "../app/utils";
-import Box from "@mui/material/Box";
 import userManager from "../app/UserManager";
 import subscriptionManager from "../app/SubscriptionManager";
 import poller from "../app/Poller";