# All files / composer/utils/draft-js-custom-plugins/unshortened-link/modifiers addUnshortenedLink.js

4% Statements 1/25
0% Branches 0/12
0% Functions 0/1
4.17% Lines 1/24

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105

    7x

     /**
     * Replace a short link with an "unshortened link" entity.
     *
     * This process is seamless for users:
     *
     * - It doesn't change the editor's SelectionState
     * - By default, it doesn't impact the undo stack – if the action is user-generated
     *   though (e.g. by clicking on a button), set the isUserAction option to true
     *   to create a new undo stack boundary
     */

    import { EditorState, Modifier, SelectionState } from '@bufferapp/draft-js';

    const addUnshortenedLink = (editorState, contentBlock, linkData, { isUserAction = false } = {}) => {
      const blockKey = contentBlock.getKey();
      let contentState = editorState.getCurrentContent();
      const originalSelectionBefore = contentState.getSelectionBefore();
      const originalSelection = editorState.getSelection();
      const currentEntityKey = contentBlock.getEntityAt(linkData.indices[0]);

      const isAlreadyUnshortenedEntity = (
        currentEntityKey !== null &&
        contentState.getEntity(currentEntityKey).getType() === 'UNSHORTENED_LINK'
      );

      if (isAlreadyUnshortenedEntity) return editorState;

      /**
       * Create and apply entity
       */
      contentState = contentState.createEntity('UNSHORTENED_LINK', 'MUTABLE', {
        shortLink: linkData.shortLink,
        unshortenedLink: linkData.unshortenedLink,
      });
      const entityKey = contentState.getLastCreatedEntityKey();

      const targetRange = new SelectionState({
        anchorKey: blockKey,
        anchorOffset: linkData.indices[0],
        focusKey: blockKey,
        focusOffset: linkData.indices[1],
      });

      contentState =
        Modifier.replaceText(contentState, targetRange, linkData.unshortenedLink, null, entityKey);

      /**
       * If current selection is after the replaced range of text, offset it
       * accordingly to the length of text we've added/removed
       */
      let newSelection = originalSelection;

      const isSelectionInTargetBlock =
        originalSelection.getAnchorKey() === targetRange.getAnchorKey();

      const isCurrentSelectionAfterReplacedRange =
        originalSelection.getAnchorOffset() >= targetRange.getFocusOffset();

      if (isSelectionInTargetBlock && isCurrentSelectionAfterReplacedRange) {
        const offset = linkData.shortLink.length - linkData.unshortenedLink.length;

        newSelection = newSelection.merge({
          anchorOffset: originalSelection.getAnchorOffset() - offset,
          focusOffset: originalSelection.getFocusOffset() - offset,
        });
      }

      /**
       * Set selectionBefore/After back to what they were before they were changed
       * by Modifier.applyEntity for proper undo/redo behavior.
       */
      contentState = contentState.merge({
        selectionBefore: originalSelectionBefore,
        selectionAfter: newSelection,
      });

      let newEditorState;

      if (isUserAction) {
        newEditorState = EditorState.push(editorState, contentState, 'apply-entity');

        newEditorState = EditorState.set(newEditorState, {
          selection: newSelection,
        });
      } else {
        /**
         * Instead of using EditorState.push() which normally should be used for pushing
         * new actions to the editor, we update the editor's state directly in order
         * to keep the undo stack as is. The original SelectionState is also preserved
         * for the process to be completely seamless to users, and lastChangeType is
         * updated to 'apply-entity' so that future keystrokes/changes are considered
         * boundary states for undo/redo.
         */
        newEditorState = EditorState.set(editorState, {
          currentContent: contentState,
          lastChangeType: 'apply-entity',
          selection: newSelection,
        });
      }

      return newEditorState;
    };

    export default addUnshortenedLink;