Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/components/timed-text-editor/CustomEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from "react";
import PropTypes from "prop-types";
import { Editor } from "draft-js";


import WrapperBlock from './WrapperBlock';

// NOTE: custom editor is in a separate class to minimise re-renders
// if considering refactoring, removing the separate class, please double check
// that doing so does not introduce uncessary re-renders first.
class CustomEditor extends React.Component {
handleWordClick = e => {
this.props.onWordClick(e);
};

renderBlockWithTimecodes = () => {
return {
component: WrapperBlock,
editable: true,
props: {
showSpeakers: this.props.showSpeakers,
showTimecodes: this.props.showTimecodes,
timecodeOffset: this.props.timecodeOffset,
editorState: this.props.editorState,
setEditorNewContentStateSpeakersUpdate: this.props.setEditorNewContentStateSpeakersUpdate,
onWordClick: this.handleWordClick,
handleAnalyticsEvents: this.props.handleAnalyticsEvents,
isEditable: this.props.isEditable
}
};
};

shouldComponentUpdate(nextProps) {
// https://stackoverflow.com/questions/39182657/best-performance-method-to-check-if-contentstate-changed-in-draftjs-or-just-edi
if (nextProps.editorState !== this.props.editorState) {
console.log('it should update??!')
return true;
}

if (nextProps.isEditable !== this.props.isEditable) {
return true;
}

return false;
}

handleOnChange = e => {
this.props.onChange(e);
};

render() {
return (
<Editor
editorState={this.props.editorState}
onChange={this.handleOnChange}
stripPastedStyles
blockRendererFn={this.renderBlockWithTimecodes}
handleKeyCommand={this.props.handleKeyCommand}
keyBindingFn={this.props.customKeyBindingFn}
spellCheck={this.props.spellCheck}
/>
);
}
}

export default CustomEditor;
4 changes: 2 additions & 2 deletions packages/components/timed-text-editor/SpeakerLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import style from './WrapperBlock.module.css';
class SpeakerLabel extends PureComponent {
render() {
return (
<span className={ style.speaker }
<span className={ this.props.isEditable? [style.speaker, style.speakerEditable].join(' '): [style.speaker, style.speakerNotEditable].join(' ')}
title={ this.props.name }
onClick={ this.props.handleOnClickEdit }>
onClick={ this.props.isEditable? this.props.handleOnClickEdit: null } >
<span className={ style.EditLabel }>
<FontAwesomeIcon icon={ faUserEdit } />
</span>
Expand Down
116 changes: 84 additions & 32 deletions packages/components/timed-text-editor/WrapperBlock.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import React from 'react';
import { EditorBlock, Modifier, EditorState, SelectionState } from 'draft-js';
import {
EditorBlock,
Modifier,
EditorState,
SelectionState,
convertFromRaw,
convertToRaw
} from 'draft-js';

import SpeakerLabel from './SpeakerLabel';
// import { shortTimecode, secondsToTimecode } from '../../Util/timecode-converter/';
Expand All @@ -11,6 +18,19 @@ import {

import style from './WrapperBlock.module.css';

const updateSpeakerName = (oldName, newName, state) => {
const contentToUpdate = convertToRaw(state);

contentToUpdate.blocks.forEach(block => {
if (block.data.speaker === oldName) {
block.data.speaker = newName;
}
})

return convertFromRaw(contentToUpdate);
}


class WrapperBlock extends React.Component {
constructor(props) {
super(props);
Expand All @@ -25,7 +45,7 @@ class WrapperBlock extends React.Component {
componentDidMount() {
const { block } = this.props;
const speaker = block.getData().get('speaker');

const start = block.getData().get('start');

this.setState({
Expand Down Expand Up @@ -55,14 +75,38 @@ class WrapperBlock extends React.Component {
return true;
}

if (nextProps.blockProps.isEditable !== this.props.blockProps.isEditable) {
return true;
}

if(nextProps.block.getData().get('speaker') !== this.state.speaker){
console.log('shouldComponentUpdate wrapper speaker', nextProps.block.getData().get('speaker') , this.state.speaker )
return true;
}

return false;
};

handleOnClickEdit = () => {
const newSpeakerName = prompt('New Speaker Name?');
componentDidUpdate = (prevProps, prevState) =>{

if(prevProps.block.getData().get('speaker') !== prevState.speaker){
console.log('componentDidUpdate wrapper speaker', prevProps.block.getData().get('speaker') , prevState.speaker );

this.setState({
speaker: prevProps.block.getData().get('speaker')
})

return true;
}
}

handleOnClickEdit = () => {
const oldSpeakerName = this.state.speaker;
const newSpeakerName = prompt('New Speaker Name?', this.state.speaker);
if (newSpeakerName !== '' && newSpeakerName !== null) {
this.setState({ speaker: newSpeakerName });
const isUpdateAllSpeakerInstances = confirm(`Would you like to change all occurrences ${oldSpeakerName} of with ${newSpeakerName} or just this one?\n\n pick OK for all, or CANCEL for just this one.`);

if (this.props.blockProps.handleAnalyticsEvents) {
this.props.blockProps.handleAnalyticsEvents({
category: 'WrapperBlock',
Expand All @@ -72,34 +116,41 @@ class WrapperBlock extends React.Component {
});
}

// From docs: https://draftjs.org/docs/api-reference-selection-state#keys-and-offsets
// selection points are tracked as key/offset pairs,
// where the key value is the key of the ContentBlock where the point is positioned
// and the offset value is the character offset within the block.

// Get key of the currentBlock
const keyForCurrentCurrentBlock = this.props.block.getKey();
// create empty selection for current block
// https://draftjs.org/docs/api-reference-selection-state#createempty
const currentBlockSelection = SelectionState.createEmpty(
keyForCurrentCurrentBlock
);
const editorStateWithSelectedCurrentBlock = EditorState.acceptSelection(
this.props.blockProps.editorState,
currentBlockSelection
);

const currentBlockSelectionState = editorStateWithSelectedCurrentBlock.getSelection();
const newBlockDataWithSpeakerName = { speaker: newSpeakerName };

// https://draftjs.org/docs/api-reference-modifier#mergeblockdata
const newContentState = Modifier.mergeBlockData(
this.props.contentState,
currentBlockSelectionState,
newBlockDataWithSpeakerName
);

this.props.blockProps.setEditorNewContentState(newContentState);
if(isUpdateAllSpeakerInstances){
const ContentState = this.props.blockProps.editorState.getCurrentContent();
const contentToUpdateWithSpekaers = updateSpeakerName(oldSpeakerName, newSpeakerName, ContentState);
this.props.blockProps.setEditorNewContentStateSpeakersUpdate(contentToUpdateWithSpekaers);
}
else{
// From docs: https://draftjs.org/docs/api-reference-selection-state#keys-and-offsets
// selection points are tracked as key/offset pairs,
// where the key value is the key of the ContentBlock where the point is positioned
// and the offset value is the character offset within the block.

// Get key of the currentBlock
const keyForCurrentCurrentBlock = this.props.block.getKey();
// create empty selection for current block
// https://draftjs.org/docs/api-reference-selection-state#createempty
const currentBlockSelection = SelectionState.createEmpty(
keyForCurrentCurrentBlock
);
const editorStateWithSelectedCurrentBlock = EditorState.acceptSelection(
this.props.blockProps.editorState,
currentBlockSelection
);

const currentBlockSelectionState = editorStateWithSelectedCurrentBlock.getSelection();
const newBlockDataWithSpeakerName = { speaker: newSpeakerName };

// https://draftjs.org/docs/api-reference-modifier#mergeblockdata
const newContentState = Modifier.mergeBlockData(
this.props.contentState,
currentBlockSelectionState,
newBlockDataWithSpeakerName
);

this.props.blockProps.setEditorNewContentStateSpeakersUpdate(newContentState);
}
}
};

Expand All @@ -125,6 +176,7 @@ class WrapperBlock extends React.Component {
<SpeakerLabel
name={ this.state.speaker }
handleOnClickEdit={ this.handleOnClickEdit }
isEditable={this.props.blockProps.isEditable}
/>
);

Expand Down
46 changes: 26 additions & 20 deletions packages/components/timed-text-editor/WrapperBlock.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@ to get text out of component with timecodes and speaker names in the interim */
}
}


.time {
font-weight: lighter;
cursor: pointer;
justify-self: start;
}

.time:hover {
text-decoration: underline;

/* color: blue; */
}

.speakerEditable {
cursor: pointer;
}

.speakerNotEditable {
cursor: no-drop;
}

.EditLabel {
margin-right: 0.5em;
}


/* Mobile devices */
@media (max-width: 768px) {

Expand Down Expand Up @@ -92,23 +118,3 @@ to get text out of component with timecodes and speaker names in the interim */
/* clear: right; */
}
}

.time {
font-weight: lighter;
cursor: pointer;
justify-self: start;
}

.time:hover {
text-decoration: underline;

/* color: blue; */
}

.speaker {
cursor: pointer;
}

.EditLabel {
margin-right: 0.5em;
}
Loading