I am using Draftjs and Draftjs mention plugin. When there is a suggestion for mention name it renders fine but if suggestion doesn't exist and I use # followed by random text it crashes. Can anyone help me out with this. Will be very grateful. Thank you.
this.mentionMembersPlugin = createMentionPlugin({
entityMutability: "IMMUTABLE",
positionSuggestions,
mentionTrigger: "#",
mentionPrefix: "",
supportWhitespace: true
});
onChangeEditor = editorState => {
this.setState({ emptyField: false });
this.setState({ editorState });
};
onSearchMemberChange = ({ value }) => {
this.setState({
suggestionMembers: defaultSuggestionsFilter(
value,
this.state.mentionMembers
)
});
};
handleKeyCommand(command: string): DraftHandleValue {
if (command === "save_teamsync") {
// Perform a request to save your contents, set
// a new `editorState`, etc.
this.add_taskComment();
return "handled";
}
return "not-handled";
}
add_taskComment() {
let newData = convertToRaw(this.state.editorState.getCurrentContent());
let checkText = newData.blocks[0].text;
this.setState({ clicked: true });
var r = JSON.stringify(
convertToRaw(this.state.editorState.getCurrentContent())
);
//e.preventDefault();
if (checkText.trim().length !== 0) {
if (this.isValid(r)) {
var data = {};
data.text = r;
data.mentionMembers = r.entityMap;
this.props
.addTaskComment(this.props.task._id, this.showTags, data)
.then(res => {
if (res.data.success) {
const editorState = EditorState.push(
this.state.editorState,
ContentState.createFromText("")
);
this.setState({
editorState: EditorState.moveFocusToEnd(editorState)
});
//this.setState({ editorState: EditorState.moveFocusToStart(EditorState.createEmpty()), clicked:false });
this.lastComment.scrollIntoView({ behavior: "smooth" });
}
});
}
} else {
this.setState({ isCommentEmpty: true });
}
}
<Editor
blockStyleFn={"myBlockStyleFn"}
editorState={this.state.editorState}
onChange={this.onChangeEditor}
plugins={this.plugins}
handleKeyCommand={this.handleKeyCommand}
keyBindingFn={this.myKeyBindingFn}
placeholder="Write a comment"
ref={element => {
this.editor = element;
}}
/>
<MentionMembersSuggestions
onSearchChange={this.onSearchMemberChange}
suggestions={this.state.suggestionMembers}
/>
This are all the code that are being used to render the comments.
This is the error I am getting "Unhandled Rejection (TypeError): this.props.getEditorState is not a function".
Related
i writen a function to convert HTML to editorState. it's work as i want but i can't use it when i assign it into the draft-wysiwyg
first function i use to call to change HTML to EditorState this function will call the other function and create EditorState on it own
function convertToEditor(markup: string): EditorState {
if (markup === '<p></p>') {
return EditorState.createEmpty()
}
const root = markupToObject(markup)
const blockContentArray: Array<ContentBlock> = []
_.forEach(root, (node, i) => {
const contentBlock = new ContentBlock({
key: genKey(),
text: node.textContent,
type: 'unstyled',
characterList: Immutable.List(getCharactorList(node.childNodes)),
data: node.getAttribute('style')?.includes('text-align') ? { 'text-align': node.getAttribute('style').split(':')[1].replace(';', '') } : {},
})
blockContentArray.push(contentBlock)
})
const contentState: ContentState = ContentState.createFromBlockArray(blockContentArray)
const editorState: EditorState = EditorState.createWithContent(contentState)
console.log(editorState)
return editorState
}
markupToObject and getCharactorList is just a function that i write to lower the complexity of the coding
function markupToObject(markup): HTMLCollection {
const div = document.createElement('div')
div.innerHTML = markup.trim()
return div.children
}
function getCharactorList(nodes: NodeListOf<ChildNode>, style?: Array<'BOLD' | 'ITALIC' | 'UNDERLINE'>): Array<CharacterMetadata> {
const characterList: Array<CharacterMetadata> = []
_.forEach(nodes, (node) => {
if (node.nodeName === '#text') {
_.forEach(node.textContent, () => {
characterList.push(CharacterMetadata.create({ style: style, entity: null }))
})
} else if (node.nodeName === 'A') {
_.forEach(node.textContent, () => {
characterList.push(CharacterMetadata.create({ style: [...(style || []), 'BOLD'], entity: null })) /* entity ID? */
})
} else {
const newStyle: Array<'BOLD' | 'ITALIC' | 'UNDERLINE'> = []
if (node.nodeName === 'STRONG') {
newStyle.push('BOLD')
} else if (node.nodeName === 'EM') {
newStyle.push('ITALIC')
} else if (node.nodeName === 'INS') {
newStyle.push('UNDERLINE')
}
characterList.push(...(getCharactorList(node.childNodes, [...newStyle, ...(style || [])]) || []))
}
})
return characterList
}
how i use
<RichText
onChange={(e) => {}}
value={convertToEditor(
'<p>text <strong>bold</strong> <strong><em>bold+italic</em></strong> </p> <p style="text-align:center;">center</p> <p><strong> link </strong></p> <p><strong> #name surname </strong></p>'
)}
/>
this is the error i got after use the Editorstate that convert from HTML
Uncaught TypeError: Cannot read properties of undefined (reading 'toList')
I am trying to show values by types, but it return only one value
filerBuilding(value) {
this.state.buildings.map((item) => {
if (value.target.value === item.buildingType) {
this.setState({
buildings: this.state.buildings.splice(this.state.buildings.length),
});
let data = [];
data.push(item);
this.setState({ buildings: data });
}
else{
}
});
}
#Giacomo's comment addresses the issue. To translate it into an implementation using filter:
function filterBuilding(value) {
const filtered = this.state.buildings.filter(
item => value.target.value === item.buildingType
);
this.setState({ buildings: filtered });
}
I am seeing a very strange behavior in my chat. Once the chat is opened, the scroll moves only a tiny bit down only when there are images in the chat. When there is only text, it goes all the way down. Also, if I close the chat and open it again, the scroll goes all the way down regardless of the content. However, If I refresh the page, the scroll returns to its weird behavior. I am puzzled as to why this is happening. Here's my code:
Here's how the chat starts:
startChat () {
document.getElementById("myForm").style.display = "block";
const ref = firebase.firestore().collection('Chats').doc(this.state.uid).collection('Messages');
const query = ref.orderBy('timestamp', 'desc').limit(10)
this.unsubFromMessages = query.onSnapshot((snapshot) => {
if (snapshot.empty) {
console.log('No matching documents.');
firebase.firestore().collection('Chats').doc(this.state.uid).
set({
name: this.state.displayName,
uid: this.state.uid,
email: this.state.email
}).then(console.log("info saved"))
.catch((error) => {
console.log("Error saving info to document: ", error);
});
}
snapshot.docChanges().reverse().forEach((change) => {
if (change.type === 'removed') {
console.log(change.doc.data().content)
}
else if (change.type === 'added') {
this.setState(state => {
const messages = [...state.messages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
else if (change.type === 'modified') {
const filteredMessages = this.state.messages.filter(message => message.body.allowed === "yes")
this.setState(state => {
const messages = [...filteredMessages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
});
}, (error) => {console.log(error)});
}
Here's the scroll function:
scrollToBottom = () => {
this.myRef.current.scrollIntoView({ behavior: "smooth" });
}
Here's the JSX of the chat:
<div className="form-popup" id="myForm">
<form className="form-container" onSubmit={this.chatFormSubmit}>
<h1>Chat</h1>
<label htmlFor="msg"><b>Message</b></label>
<div className="chatArea" id='messages'>
{
this.state.messages.map((message, index) => {
return message.body.uid === this.state.uid && !message.body.imageUrl
?
<p className="message-sent" key={index}>{message.body.content}</p>
:
message.body.uid === this.state.uid && message.body.imageUrl
?
<img src={message.body.imageUrl} className="message-sent" key={index}></img>
:
<p className="message-received" key={index}>{message.body.content}</p>
})
}
<div style={{ float:"left", clear: "both" }}
ref={this.myRef}>
</div>
</div>
And if the functions for closing and submitting messages to the chat are of any use, here they are:
closeForm() {
document.getElementById("myForm").style.display = "none";
this.setState({messages: []})
this.unsubFromMessages();
}
chatFormSubmit(e) {
e.preventDefault();
this.setState({ writeError: null });
firebase.firestore()
.collection('Chats')
.doc(this.state.uid)
.collection('Messages')
.doc()
.set({
docId: this.state.docId,
content: this.chatArea.current.value,
allowed: "yes",
timestamp: new Date(),
uid: this.state.uid,
name: this.state.displayName,
email: this.state.email
}, { merge: true })
.catch((error) => {
this.setState({ writeError: error.message });
})
.then(this.chatArea.current.value = '')
}
Again, I figured it out myself. Instead of calling "this.scrollToBottom()" in setTimeout, I should have simply passed it like this setTimeout( this.scrollToBottom, 2000). That is why setTimeout was not working and the scroll stopped half way. Credit goes to Felix Kling's comment in ReactJS: setTimeout() not working?.
I'm trying to update state using handleChangeProps method, but some how finally the fields.fileName is set as empty string instead of actual value. I'm using material-ui DropZone for file and for name TextField. The addNsmFile is called when onSubmit is called. The remaining fields: name, fileData are not empty and the actual value is set and I can get them in addNsmFile function. Can you help me to figure out why fields.fileName is set as empty finally?
const [fields, setFields] = useState({
name : '',
fileName: '',
fileData: ''
})
const handleChangeProps = (name, value) => {
if (name === 'name' ) {
if (validator.isEmpty(value.trim())) {
setNameError('Enter NSM Name')
} else if (value.trim().length > 512) {
setNameError('The maximum length for NSM name is 512. Please re-enter.')
} else {
setNameError('')
}
}
if (name === 'fileData') {
console.log('fileData', value)
if (validator.isEmpty(value.trim())) {
setFileuploadError('Drag and drop or browse nsm file')
} else {
setFileuploadError('')
}
}
setFields({ ...fields, [name]: value })
}
const addNsmFile = () =>{
let nsmForm = new FormData()
nsmForm.append('name', fields.name)
nsmForm.append('fileName', fields.fileName)
nsmForm.append('fileData', fields.fileData)
NSMDataService.addFile(nsmForm).then((response)=>{
if (response.data.substring(0, 1) === '0') {
const results = JSON.parse(response.data.substring(2))
addNotification('success', 'NSM file is being added successfully.')
//props.onRowSelectionChange(results.addFile)
props.setOpenDialog(false)
props.setRefresh(results.addFile[0].id)
} else if (response.data.substring(0, 1) === '1') {
addNotification('error', response.data.substring(2))
props.setOpenDialog(false)
}
}).catch((error)=>{
console.log(error)
})
}
<DropzoneArea
acceptedFiles={[".csv, text/csv, application/vnd.ms-excel, application/csv, text/x-csv, application/x-csv, text/comma-separated-values, text/x-comma-separated-values"]}
maxFileSize={1000000000} //1GB
dropzoneText='Drag and drop a NSM file here or click to add'
showFileNames= {true}
showPreviews={false}
useChipsForPreview={true}
showAlerts={true}
filesLimit={1}
classes={{root:classes.rootDropzoneArea, icon: classes.iconDropzoneArea, text: classes.textDropzoneArea}}
onChange={(files) => {
files.forEach((file) => {
console.log(file)
handleChangeProps('fileName', file.name)
let reader = new FileReader()
reader.onload = function(event) {
let contents = event.target.result
handleChangeProps('fileData', contents)
console.log("File contents: " + contents)
}
reader.onerror = function(event) {
console.error("File could not be read! Code " + event.target.error.code)
}
reader.readAsText(file);
})
}}
onDelete={(file)=> {
handleChangeProps('fileData', '')
}
}
/>
I think the problem might be with your setFields() update in handleChangeProps. Try to do like this:
setFields(prevState => ({
...prevState,
[name]: value,
}))
I have 2 Components: Community.js and Edit.js
I call to Edit from Community below:
<DetailModal
DetailModal={Detail}
errors={this.state.errors}
uploadFile={this.props.uploadFileActions.uploadFile}
onSave={this.save}
onChange={this.onChange}
mode={this.state.mode}
data={this.state.details}
isOpen={this.state.modalIsOpen}
closeModal={this.closeModal}
editable={isHasEditPermisson}
/>
At Community, I have a function onchange() below:
onChange = (field, data) => {
let value = null;
if (data) {
value = data
}
this.setState(state => ({
details: {
...state.details,
[field]: value
},
errors: {
...state.errors,
[field]: undefined
}
}));
// }
}
At Edit, I have a function which called to select image/video file:
selectFile = (file) => {
if (file && file.target.files.length > 0) {
const checkType = file.target.files[0].type.split('/')[0]
const extendType = file.target.files[0].type.split('/')[1]
const fileArr = [];
// if (checkType === "video") {
// console.log('this.getDuration(file)', this.getDuration(file))
// if (this.getDuration(file) > 60) {
// alert("stop");
// return;
// }
// }
// this.props.uploadFile(file.target.files[0], (res) => {
// this.props.onChange('ResourceUrl', `${this.props.data.ResourceUrl ? `${this.props.data.ResourceUrl};` : ''}${res.data.Data}`);
// });
fileArr.push({
file: file.target.files[0],
urlFile: URL.createObjectURL(file.target.files[0]),
});
this.props.onChange('ResourceUrl', `${this.props.data.ResourceUrl ? `${this.props.data.ResourceUrl};` : ''}${fileArr[0].urlFile}`);
this.props.onChange('ResourceFile', this.props.data.ResourceFile ? this.props.data.ResourceFile : fileArr[0].file);
if (checkType === "image") {
this.setState({
fileType: "image/*",
extend: extendType
})
} else {
this.setState({
fileType: "video/*",
countVideo: 1,
extend: extendType
})
}
// file.target.value = '';
}
}
This is Init state in Community:
constructor(props) {
super(props);
this.escFunction = this.escFunction.bind(this);
this.state = {
modalIsOpen: false,
mode: 'add',
details: {},
errors: {},
defaultRole: constants.CollaboratorRole.default,
permanentRole: constants.CollaboratorRole.permanent,
isOpenDeleteConfirm: false
};
}
Here, I call to onchange() in Community to set value for 2 field: ResourceUrl, ResourceFile
But I have an issue when set value for ResourceFile. When I choose second file then I still get value of first file.
I don't know how to set the value of the second file into ResourceFile, which means that I expect that ResourceFile is an array containing the information of the two files I just selected.