finish emote picker

This commit is contained in:
smolgrrr 2024-08-18 21:39:43 +10:00
parent 665b9f33c2
commit 8f94736969
4 changed files with 125 additions and 46542 deletions

View File

@ -1,14 +1,12 @@
import { import {
CpuChipIcon, CpuChipIcon
PlusCircleIcon
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { XCircleIcon } from "@heroicons/react/24/solid"; import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools"; import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools";
import { renderMedia } from "../../utils/FileUpload";
import { useSubmitForm } from "./handleSubmit"; import { useSubmitForm } from "./handleSubmit";
import "../../styles/Form.css"; import "../../styles/Form.css";
import EmotePicker from "../modals/EmotePicker/EmotePicker"; import EmotePicker from "../modals/EmotePicker/EmotePicker";
import emotes from "../modals/EmotePicker/custom_emojis.json"
interface FormProps { interface FormProps {
refEvent?: NostrEvent; refEvent?: NostrEvent;
@ -21,9 +19,7 @@ const NewNoteCard = ({
tagType, tagType,
hashtag, hashtag,
}: FormProps) => { }: FormProps) => {
const ref = useRef<HTMLDivElement | null>(null);
const [comment, setComment] = useState(""); const [comment, setComment] = useState("");
const [file, setFile] = useState("");
const [unsigned, setUnsigned] = useState<UnsignedEvent>({ const [unsigned, setUnsigned] = useState<UnsignedEvent>({
kind: 1, kind: 1,
tags: [ tags: [
@ -39,8 +35,6 @@ const NewNoteCard = ({
const [difficulty, setDifficulty] = useState( const [difficulty, setDifficulty] = useState(
localStorage.getItem("difficulty") || "21" localStorage.getItem("difficulty") || "21"
); );
const [fileSizeError, setFileSizeError] = useState(false);
const [uploadingFile, setUploadingFile] = useState(false);
useEffect(() => { useEffect(() => {
if (hashtag) { if (hashtag) {
@ -79,17 +73,16 @@ const NewNoteCard = ({
useEffect(() => { useEffect(() => {
setUnsigned(prevUnsigned => ({ setUnsigned(prevUnsigned => ({
...prevUnsigned, ...prevUnsigned,
content: `${comment} ${file}`, content: `${comment}`,
created_at: Math.floor(Date.now() / 1000), created_at: Math.floor(Date.now() / 1000),
})); }));
}, [comment, file]); }, [comment]);
const { handleSubmit: originalHandleSubmit, doingWorkProp, doingWorkProgress } = useSubmitForm(unsigned, difficulty); const { handleSubmit: originalHandleSubmit, doingWorkProp, doingWorkProgress } = useSubmitForm(unsigned, difficulty);
const handleSubmit = async (event: React.FormEvent) => { const handleSubmit = async (event: React.FormEvent) => {
await originalHandleSubmit(event); await originalHandleSubmit(event);
setComment(""); setComment("");
setFile("");
setUnsigned(prevUnsigned => ({ setUnsigned(prevUnsigned => ({
...prevUnsigned, ...prevUnsigned,
content: '', content: '',
@ -97,6 +90,24 @@ const NewNoteCard = ({
})); }));
}; };
//Emoji stuff
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
interface Emoji {
category: string;
shortcode: string;
static_url: string;
tags: string[];
url: string;
visible_in_picker: boolean;
}
async function onEmojiSelect(emoji: Emoji) {
setShowEmojiPicker(false);
setComment(comment + " :" + emoji.shortcode + ":");
unsigned.tags.push(['emoji', emoji.shortcode, emoji.url]);
}
return ( return (
<form <form
name="post" name="post"
@ -115,14 +126,6 @@ const NewNoteCard = ({
onChange={(e) => setComment(e.target.value)} onChange={(e) => setComment(e.target.value)}
rows={comment.split('\n').length || 1} rows={comment.split('\n').length || 1}
/> />
<div className="relative">
{file !== "" && (
<button onClick={() => setFile("")}>
<XCircleIcon className="h-10 w-10 absolute shadow z-100 text-blue-500" />
</button>
)}
{renderMedia([file])}
</div>
<div className="h-14 flex items-center justify-between"> <div className="h-14 flex items-center justify-between">
<div className="inline-flex items-center gap-2 bg-neutral-800 px-1.5 py-1 rounded-lg"> <div className="inline-flex items-center gap-2 bg-neutral-800 px-1.5 py-1 rounded-lg">
<div className="inline-flex items-center gap-1.5 text-neutral-300"> <div className="inline-flex items-center gap-1.5 text-neutral-300">
@ -134,11 +137,11 @@ const NewNoteCard = ({
</div> </div>
<div> <div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<EmotePicker /> <EmotePicker onEmojiSelect={(emoji: Emoji) => onEmojiSelect(emoji)} />
<button <button
type="submit" type="submit"
className={`bg-black border h-9 inline-flex items-center justify-center px-4 rounded-lg text-white font-medium text-sm ${doingWorkProp || uploadingFile ? 'cursor-not-allowed' : ''}`} className={`bg-black border h-9 inline-flex items-center justify-center px-4 rounded-lg text-white font-medium text-sm ${doingWorkProp ? 'cursor-not-allowed' : ''}`}
disabled={doingWorkProp || uploadingFile} disabled={doingWorkProp}
> >
Submit Submit
</button> </button>
@ -146,9 +149,6 @@ const NewNoteCard = ({
</div> </div>
</div> </div>
</div> </div>
{fileSizeError ? (
<span className="text-red-500">File size should not exceed 2.5MB</span>
) : null}
{doingWorkProp ? ( {doingWorkProp ? (
<div className="flex animate-pulse text-sm text-gray-300"> <div className="flex animate-pulse text-sm text-gray-300">
<CpuChipIcon className="h-4 w-4 ml-auto" /> <CpuChipIcon className="h-4 w-4 ml-auto" />

View File

@ -1,27 +1,115 @@
import React, { useState } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import emotes from './custom_emojis.json'; import emotes from './custom_emojis.json';
import { PlusCircleIcon } from '@heroicons/react/24/outline'; import { PlusCircleIcon } from '@heroicons/react/24/outline';
const EmotePicker = () => { interface Emoji {
category: string;
shortcode: string;
static_url: string;
tags: string[];
url: string;
visible_in_picker: boolean;
}
interface EmotePickerProps {
onEmojiSelect?: (emoji: Emoji) => void;
}
const EmotePicker: React.FC<EmotePickerProps> = ({ onEmojiSelect }) => {
const [showEmotes, setShowEmotes] = useState(false); const [showEmotes, setShowEmotes] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [hoveredEmote, setHoveredEmote] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState(0);
const modalRef = useRef<HTMLDivElement>(null);
const toggleEmotes = () => { const toggleEmotes = () => {
setShowEmotes(!showEmotes); setShowEmotes(!showEmotes);
}; };
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value);
};
const categories = [
{ label: '0-4', range: /^[0-4]/ },
{ label: '5-9', range: /^[5-9]/ },
{ label: 'A-E', range: /^[A-E]/i },
{ label: 'F-J', range: /^[F-J]/i },
{ label: 'K-N', range: /^[K-N]/i },
{ label: 'O-R', range: /^[O-R]/i },
{ label: 'S-V', range: /^[S-V]/i },
{ label: 'W-Z', range: /^[W-Z]/i },
];
const filteredEmotes = emotes.filter((emote) => {
const matchesSearch = emote.shortcode.toLowerCase().includes(searchQuery.toLowerCase());
if (searchQuery) {
return matchesSearch;
} else {
const category = categories[activeTab];
return matchesSearch && category.range.test(emote.shortcode);
}
});
useEffect(() => {
// Explicitly type the event parameter
function handleClickOutside(event: MouseEvent) {
// Use a type assertion to tell TypeScript the target is an HTMLElement
const target = event.target as HTMLElement;
if (modalRef.current && !modalRef.current.contains(target)) {
setShowEmotes(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []); // Removed modalRef from dependency array as it doesn't change
return ( return (
<> <div className="static m-auto">
<PlusCircleIcon className="h-4 w-4 text-neutral-400 cursor-pointer" onClick={toggleEmotes} /> <PlusCircleIcon className="h-4 w-4 text-neutral-400 cursor-pointer" onClick={toggleEmotes} />
{showEmotes && ( {showEmotes && (
<div className="flex flex-wrap mt-2 border"> <div ref={modalRef} className="absolute flex flex-wrap m-4 p-2 rounded-lg sm:w-full md:w-full lg:w-1/4 bg-[#151617] max-h-64 overflow-y-auto max-w-full right-0 left-0 mx-auto">
{emotes.slice(0, 20).map((emote, index) => ( <input
<div key={index} className="w-1/5 p-1 text-center"> type="text"
<img src={emote.static_url} alt={emote.shortcode} className="w-5 h-5" /> placeholder="Search emotes..."
value={searchQuery}
onChange={handleSearch}
className="w-full mb-2 h-10 px-4 py-2 bg-gray-900 rounded-md shadow-sm focus:outline-none focus:border-blue-500"
/>
<div className="flex w-full mb-2">
{categories.map((category, index) => (
<button
type="button"
key={index}
className={`text-xs px-2 py-1 rounded-md ${activeTab === index ? 'bg-gray-700' : 'bg-gray-600'
}`}
onClick={() => setActiveTab(index)}
>
{category.label}
</button>
))}
</div>
{filteredEmotes.map((emote: Emoji, index) => (
<div
key={index}
className="text-center relative"
onMouseEnter={() => setHoveredEmote(emote.shortcode)}
onMouseLeave={() => setHoveredEmote(null)}
onClick={() => onEmojiSelect && onEmojiSelect(emote)} // Add this line
>
<img src={emote.static_url} className="w-7 m-1" />
{hoveredEmote === emote.shortcode && (
<div className="absolute bottom-full left-1/2 -translate-x-1/2 bg-gray-800 text-white px-2 py-1 rounded-md">
{emote.shortcode}
</div>
)}
</div> </div>
))} ))}
</div> </div>
)} )}
</> </div>
); );
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
#!/bin/bash
# Read the JSON file
json=$(cat custom_emojis.json)
# Create a temporary file to store the filtered JSON
tmp_file=$(mktemp)
# Initialize a counter for successful URLs
success_count=0
echo "Filtering emojis..."
# Loop through each object in the JSON array
echo "$json" | jq -c '.[]' | while read obj; do
# Extract the url from the object
url=$(echo "$obj" | jq -r '.url')
# Check the response headers of the URL using curl
response=$(curl --silent --head "$url")
status_code=$(echo "$response" | grep -o 'HTTP/[0-9.]* [0-9]*' | awk '{print $2}')
# Check if the response status code is 200 (OK)
if [ "$status_code" -eq 200 ]; then
# Check if the response headers contain the desired content type
if echo "$response" | grep -q "Content-Type: image/"; then
echo "$obj" >> "$tmp_file"
((success_count++))
echo "Successful URL: $url"
else
echo "Failed URL: $url (Incorrect content type)"
fi
else
echo "Failed URL: $url (Status code: $status_code)"
fi
done
echo "$success_count emojis with successful URLs"
# Replace the original JSON file with the filtered version
echo "Updating custom_emojis.json..."
cat "$tmp_file" | jq -c '. | {shortcode, url}' > custom_emojis.json
rm "$tmp_file"
echo "Done!"