mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
fix emoji
This commit is contained in:
parent
c68924af04
commit
e02ccd0edf
25
src/common/Portal.tsx
Normal file
25
src/common/Portal.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
|
interface PortalProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const Portal: React.FC<PortalProps> = ({ children }) => {
|
||||||
|
const [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true)
|
||||||
|
|
||||||
|
return () => setMounted(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return mounted
|
||||||
|
? createPortal(
|
||||||
|
children,
|
||||||
|
document.querySelector('#modal-root') as HTMLElement
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Portal
|
@ -4,11 +4,11 @@ import './styles.css';
|
|||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
|
|
||||||
const extractComponents = (url) => {
|
const extractComponents = (url) => {
|
||||||
if (!url.startsWith("qortal://")) {
|
if (!url || !url.startsWith("qortal://")) { // Check if url exists and starts with "qortal://"
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
url = url.replace(/^(qortal\:\/\/)/, "");
|
url = url.replace(/^(qortal\:\/\/)/, ""); // Safe to use replace now
|
||||||
if (url.includes("/")) {
|
if (url.includes("/")) {
|
||||||
let parts = url.split("/");
|
let parts = url.split("/");
|
||||||
const service = parts[0].toUpperCase();
|
const service = parts[0].toUpperCase();
|
||||||
@ -23,6 +23,7 @@ const extractComponents = (url) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function processText(input) {
|
function processText(input) {
|
||||||
const linkRegex = /(qortal:\/\/\S+)/g;
|
const linkRegex = /(qortal:\/\/\S+)/g;
|
||||||
function processNode(node) {
|
function processNode(node) {
|
||||||
@ -58,6 +59,8 @@ function processText(input) {
|
|||||||
|
|
||||||
export const MessageDisplay = ({ htmlContent, isReply }) => {
|
export const MessageDisplay = ({ htmlContent, isReply }) => {
|
||||||
const linkify = (text) => {
|
const linkify = (text) => {
|
||||||
|
if (!text) return ""; // Return an empty string if text is null or undefined
|
||||||
|
|
||||||
let textFormatted = text;
|
let textFormatted = text;
|
||||||
const urlPattern = /(\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+)/g;
|
const urlPattern = /(\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+)/g;
|
||||||
textFormatted = text.replace(urlPattern, (url) => {
|
textFormatted = text.replace(urlPattern, (url) => {
|
||||||
@ -66,6 +69,7 @@ export const MessageDisplay = ({ htmlContent, isReply }) => {
|
|||||||
});
|
});
|
||||||
return processText(textFormatted);
|
return processText(textFormatted);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const sanitizedContent = DOMPurify.sanitize(linkify(htmlContent), {
|
const sanitizedContent = DOMPurify.sanitize(linkify(htmlContent), {
|
||||||
ALLOWED_TAGS: [
|
ALLOWED_TAGS: [
|
||||||
|
@ -5,10 +5,23 @@
|
|||||||
.emoji-picker {
|
.emoji-picker {
|
||||||
position: absolute; /* Picker positioned absolutely relative to the parent */
|
position: absolute; /* Picker positioned absolutely relative to the parent */
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 1000; /* Ensure picker appears above other content */
|
z-index: 9000000000; /* Ensure picker appears above other content */
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-container {
|
.message-container {
|
||||||
overflow: visible; /* Ensure the message container doesn't cut off the picker */
|
overflow: visible; /* Ensure the message container doesn't cut off the picker */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.reaction-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-picker {
|
||||||
|
overflow: hidden;
|
||||||
|
width: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.EmojiPickerReact.epr-dark-theme {
|
||||||
|
--epr-emoji-size: 18px; /* Adjust emoji size for dark mode */
|
||||||
|
}
|
@ -1,39 +1,65 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import Picker, { Theme } from 'emoji-picker-react';
|
import ReactDOM from 'react-dom';
|
||||||
import './ReactionPicker.css'; // CSS for proper positioning
|
import Picker, { EmojiStyle, Theme } from 'emoji-picker-react';
|
||||||
|
import './ReactionPicker.css';
|
||||||
import { ButtonBase } from '@mui/material';
|
import { ButtonBase } from '@mui/material';
|
||||||
import { isMobile } from '../App';
|
import { isMobile } from '../App';
|
||||||
|
|
||||||
export const ReactionPicker = ({ onReaction }) => {
|
export const ReactionPicker = ({ onReaction }) => {
|
||||||
const [showPicker, setShowPicker] = useState(false); // Manage picker visibility
|
const [showPicker, setShowPicker] = useState(false);
|
||||||
const pickerRef = useRef(null); // Reference to the picker
|
const [pickerPosition, setPickerPosition] = useState({ top: 0, left: 0 });
|
||||||
|
const pickerRef = useRef(null);
|
||||||
|
const buttonRef = useRef(null);
|
||||||
|
|
||||||
const handleReaction = (emojiObject) => {
|
const handleReaction = (emojiObject) => {
|
||||||
onReaction(emojiObject.emoji); // Handle the selected emoji reaction
|
onReaction(emojiObject.emoji);
|
||||||
setShowPicker(false); // Close picker after selection
|
setShowPicker(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePicker = (emojiObject) => {
|
const handlePicker = (emojiObject) => {
|
||||||
|
onReaction(emojiObject.emoji);
|
||||||
onReaction(emojiObject.emoji); // Handle the selected emoji reaction
|
setShowPicker(false);
|
||||||
setShowPicker(false); // Close picker after selection
|
};
|
||||||
|
|
||||||
|
const togglePicker = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (showPicker) {
|
||||||
|
setShowPicker(false);
|
||||||
|
} else {
|
||||||
|
// Get the button's position
|
||||||
|
const buttonRect = buttonRef.current.getBoundingClientRect();
|
||||||
|
const pickerWidth = isMobile ? 300 : 350; // Adjust based on picker width
|
||||||
|
|
||||||
|
// Calculate position to align the right edge of the picker with the button's right edge
|
||||||
|
setPickerPosition({
|
||||||
|
top: buttonRect.bottom + window.scrollY, // Position below the button
|
||||||
|
left: buttonRect.right + window.scrollX - pickerWidth, // Align right edges
|
||||||
|
});
|
||||||
|
setShowPicker(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close picker if clicked outside
|
// Close picker if clicked outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
if (pickerRef.current && !pickerRef.current.contains(event.target)) {
|
if (
|
||||||
setShowPicker(false); // Close picker
|
pickerRef.current &&
|
||||||
|
!pickerRef.current.contains(event.target) &&
|
||||||
|
buttonRef.current &&
|
||||||
|
!buttonRef.current.contains(event.target)
|
||||||
|
) {
|
||||||
|
setShowPicker(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listener when picker is shown
|
|
||||||
if (showPicker) {
|
if (showPicker) {
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
} else {
|
} else {
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the event listener on unmount
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
};
|
};
|
||||||
@ -42,42 +68,41 @@ export const ReactionPicker = ({ onReaction }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="reaction-container">
|
<div className="reaction-container">
|
||||||
{/* Emoji CTA */}
|
{/* Emoji CTA */}
|
||||||
<ButtonBase sx={{
|
<ButtonBase
|
||||||
fontSize: '22px'
|
sx={{ fontSize: '22px' }}
|
||||||
}}
|
ref={buttonRef}
|
||||||
// onTouchStart={(e) => {
|
onClick={togglePicker}
|
||||||
// e.preventDefault(); // Prevent mobile keyboard
|
|
||||||
// e.stopPropagation();
|
|
||||||
// if(!isMobile) return
|
|
||||||
// setShowPicker(!showPicker);
|
|
||||||
// }}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault(); // Prevents any focus issues
|
|
||||||
e.stopPropagation();
|
|
||||||
// if(isMobile) return
|
|
||||||
|
|
||||||
setShowPicker(!showPicker);
|
|
||||||
}}
|
|
||||||
|
|
||||||
>
|
>
|
||||||
😃
|
😃
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
{/* Emoji Picker with dark theme */}
|
{/* Emoji Picker rendered in a portal with calculated position */}
|
||||||
{showPicker && (
|
{showPicker &&
|
||||||
<div className="emoji-picker" ref={pickerRef} onClick={(e) => e.preventDefault()}>
|
ReactDOM.createPortal(
|
||||||
<Picker
|
<div
|
||||||
height={isMobile ? 350 : 450}
|
className="emoji-picker"
|
||||||
width={isMobile ? 300 : 350 }
|
ref={pickerRef}
|
||||||
reactionsDefaultOpen={true}
|
style={{
|
||||||
onReactionClick={handleReaction}
|
position: 'absolute',
|
||||||
onEmojiClick={handlePicker}
|
top: pickerPosition.top,
|
||||||
allowExpandReactions={true}
|
left: pickerPosition.left,
|
||||||
autoFocusSearch={false}
|
zIndex: 1000,
|
||||||
theme={Theme.DARK}
|
}}
|
||||||
/>
|
>
|
||||||
</div>
|
<Picker
|
||||||
)}
|
height={isMobile ? 350 : 450}
|
||||||
|
width={isMobile ? 300 : 350}
|
||||||
|
reactionsDefaultOpen={true}
|
||||||
|
onReactionClick={handleReaction}
|
||||||
|
onEmojiClick={handlePicker}
|
||||||
|
allowExpandReactions={true}
|
||||||
|
autoFocusSearch={false}
|
||||||
|
theme={Theme.DARK}
|
||||||
|
emojiStyle={EmojiStyle.NATIVE}
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -107,4 +107,5 @@ html, body {
|
|||||||
|
|
||||||
.swiper {
|
.swiper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user