function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
 * 
 * @file Activity feed sidebar component
 * @author Box
 */

import * as React from 'react';
import classNames from 'classnames';
import debounce from 'lodash/debounce';
import flow from 'lodash/flow';
import getProp from 'lodash/get';
import noop from 'lodash/noop';
import uniqueId from 'lodash/uniqueId';
import { FormattedMessage } from 'react-intl';
import { generatePath } from 'react-router-dom';
import ActivityFeed from './activity-feed';
// $FlowFixMe
import ActivityFeedV2 from './activity-feed-v2';
import AddTaskButton from './AddTaskButton';
import API from '../../api';
import messages from '../common/messages';
import SidebarContent from './SidebarContent';
import { EVENT_DATA_READY, EVENT_JS_READY } from '../common/logger/constants';
import { getBadUserError } from '../../utils/error';
import { mark } from '../../utils/performance';
import { withAnnotatorContext } from '../common/annotator-context';
import { withAPIContext } from '../common/api-context';
import { withErrorBoundary } from '../common/error-boundary';
import { withFeatureConsumer, isFeatureEnabled } from '../common/feature-checking';
import { withLogger } from '../common/logger';
import { withRouterAndRef } from '../common/routing';
import ActivitySidebarFilter from './ActivitySidebarFilter';
import { ViewType, FeedEntryType } from '../common/types/SidebarNavigation';
import { ACTIVITY_FILTER_OPTION_ALL, ACTIVITY_FILTER_OPTION_RESOLVED, ACTIVITY_FILTER_OPTION_TASKS, ACTIVITY_FILTER_OPTION_UNRESOLVED, DEFAULT_COLLAB_DEBOUNCE, ERROR_CODE_FETCH_ACTIVITY, FEED_ITEM_TYPE_ANNOTATION, FEED_ITEM_TYPE_COMMENT, FEED_ITEM_TYPE_TASK, FEED_ITEM_TYPE_VERSION, ORIGIN_ACTIVITY_SIDEBAR, SIDEBAR_VIEW_ACTIVITY, TASK_COMPLETION_RULE_ALL, METRIC_TYPE_UAA_PARITY_METRIC } from '../../constants';
import './ActivitySidebar.scss';
export const activityFeedInlineError = {
  inlineError: {
    title: messages.errorOccured,
    content: messages.activityFeedItemApiError
  }
};
const MARK_NAME_DATA_LOADING = `${ORIGIN_ACTIVITY_SIDEBAR}_data_loading`;
const MARK_NAME_DATA_READY = `${ORIGIN_ACTIVITY_SIDEBAR}_${EVENT_DATA_READY}`;
const MARK_NAME_JS_READY = `${ORIGIN_ACTIVITY_SIDEBAR}_${EVENT_JS_READY}`;
mark(MARK_NAME_JS_READY);
class ActivitySidebar extends React.PureComponent {
  constructor(props) {
    super(props);
    // eslint-disable-next-line react/prop-types
    _defineProperty(this, "handleAnnotationDelete", ({
      id,
      permissions
    }) => {
      const {
        api,
        emitAnnotationRemoveEvent,
        file
      } = this.props;
      emitAnnotationRemoveEvent(id, true);
      api.getFeedAPI(false).deleteAnnotation(file, id, permissions, this.deleteAnnotationSuccess.bind(this, id), this.feedErrorCallback);
      this.fetchFeedItems();
    });
    _defineProperty(this, "handleAnnotationEdit", ({
      id,
      text,
      permissions
    }) => {
      const {
        api,
        emitAnnotationUpdateEvent,
        file
      } = this.props;
      emitAnnotationUpdateEvent({
        id,
        description: {
          message: text
        }
      }, true);
      api.getFeedAPI(false).updateAnnotation(file, id, text, undefined, permissions, annotation => {
        emitAnnotationUpdateEvent(annotation);
        this.feedSuccessCallback();
      }, this.feedErrorCallback);
      this.fetchFeedItems();
    });
    _defineProperty(this, "handleAnnotationStatusChange", ({
      id,
      permissions,
      status
    }) => {
      const {
        api,
        emitAnnotationUpdateEvent,
        file
      } = this.props;
      emitAnnotationUpdateEvent({
        id,
        status
      }, true);
      api.getFeedAPI(false).updateAnnotation(file, id, undefined, status, permissions, annotation => {
        emitAnnotationUpdateEvent(annotation);
        this.feedSuccessCallback();
      }, this.feedErrorCallback);
      this.fetchFeedItems();
    });
    /**
     * Success callback for fetching feed items
     */
    _defineProperty(this, "feedSuccessCallback", () => {
      this.fetchFeedItems();
    });
    /**
     * Error callback for fetching feed items
     *
     * @param {Error} e - the error which occured
     * @param {Error} code - the code for the error
     * @param {Object} contextInfo - the context info for the error
     */
    _defineProperty(this, "feedErrorCallback", (e, code, contextInfo) => {
      this.errorCallback(e, code, contextInfo);
      this.fetchFeedItems();
    });
    _defineProperty(this, "createTask", (message, assignees, taskType, dueAt, completionRule, onSuccess, onError) => {
      const {
        api,
        currentUser,
        file
      } = this.props;
      if (!currentUser) {
        throw getBadUserError();
      }
      const errorCallback = (e, code, contextInfo) => {
        if (onError) {
          onError(e, code, contextInfo);
        }
        this.feedErrorCallback(e, code, contextInfo);
      };
      const successCallback = () => {
        if (onSuccess) {
          onSuccess();
        }
        this.feedSuccessCallback();
      };
      api.getFeedAPI(false).createTaskNew(file, currentUser, message, assignees, taskType, dueAt, completionRule, successCallback, errorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    _defineProperty(this, "deleteTask", task => {
      const {
        file,
        api,
        onTaskDelete
      } = this.props;
      api.getFeedAPI(false).deleteTaskNew(file, task, taskId => {
        this.feedSuccessCallback();
        onTaskDelete(taskId);
      }, this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    _defineProperty(this, "updateTask", (task, onSuccess, onError) => {
      const {
        api,
        file,
        onTaskUpdate
      } = this.props;
      const errorCallback = (e, code) => {
        if (onError) {
          onError(e, code);
        }
        this.feedErrorCallback(e, code);
      };
      const successCallback = () => {
        this.feedSuccessCallback();
        if (onSuccess) {
          onSuccess();
        }
        onTaskUpdate();
      };
      api.getFeedAPI(false).updateTaskNew(file, task, successCallback, errorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    _defineProperty(this, "updateTaskAssignment", (taskId, taskAssignmentId, status) => {
      const {
        api,
        currentUser = {},
        file,
        onTaskAssignmentUpdate
      } = this.props;
      const successCallback = () => {
        this.feedSuccessCallback();
        onTaskAssignmentUpdate(taskId, taskAssignmentId, status, currentUser.id);
      };
      api.getFeedAPI(false).updateTaskCollaborator(file, taskId, taskAssignmentId, status, successCallback, this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    _defineProperty(this, "getTaskCollaborators", task => {
      const {
        api,
        file
      } = this.props;
      return new Promise((resolve, reject) => {
        api.getTaskCollaboratorsAPI(false).getTaskCollaborators({
          errorCallback: reject,
          file: {
            id: file.id
          },
          successCallback: resolve,
          task: {
            id: task.id
          }
        });
      });
    });
    /**
     * Deletes a comment via the API.
     *
     * @param {Object} args - A subset of the comment
     * @return void
     */
    _defineProperty(this, "deleteComment", ({
      id,
      permissions
    }) => {
      const {
        api,
        features,
        file,
        hasReplies,
        onCommentDelete
      } = this.props;
      const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled');
      const successCallback = comment => {
        this.feedSuccessCallback();
        onCommentDelete(comment);
      };
      if (hasReplies || isThreadedRepliesV2Enabled) {
        api.getFeedAPI(false).deleteThreadedComment(file, id, permissions, successCallback, this.feedErrorCallback);
      } else {
        api.getFeedAPI(false).deleteComment(file, id, permissions, successCallback, this.feedErrorCallback);
      }

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Deletes a reply via the API.
     *
     * @param {Object} args - A subset of the comment
     * @return void
     */
    _defineProperty(this, "deleteReply", ({
      id,
      parentId,
      permissions
    }) => {
      const {
        api,
        emitAnnotationReplyDeleteEvent,
        file
      } = this.props;
      emitAnnotationReplyDeleteEvent(id, parentId, true);
      api.getFeedAPI(false).deleteReply(file, id, parentId, permissions, this.deleteReplySuccessCallback.bind(this, id, parentId), this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Handles a successful deletion of a reply
     *
     * @private
     * @param {string} id - The id of the reply
     * @param {string} parentId - The id of the reply's parent item
     * @return {void}
     */
    _defineProperty(this, "deleteReplySuccessCallback", (id, parentId) => {
      const {
        emitAnnotationReplyDeleteEvent
      } = this.props;
      this.feedSuccessCallback();
      emitAnnotationReplyDeleteEvent(id, parentId);
    });
    _defineProperty(this, "updateComment", (id, text, status, hasMention, permissions, onSuccess, onError) => {
      const {
        api,
        features,
        file,
        hasReplies,
        onCommentUpdate
      } = this.props;
      const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled');
      const errorCallback = (e, code) => {
        if (onError) {
          onError(e, code);
        }
        this.feedErrorCallback(e, code);
      };
      const successCallback = () => {
        this.feedSuccessCallback();
        if (onSuccess) {
          onSuccess();
        }
        onCommentUpdate();
      };
      if (hasReplies || isThreadedRepliesV2Enabled) {
        api.getFeedAPI(false).updateThreadedComment(file, id, text, status, permissions, successCallback, errorCallback);
      } else {
        api.getFeedAPI(false).updateComment(file, id, text || '', hasMention, permissions, successCallback, errorCallback);
      }

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Updates a reply
     *
     * @param {string} id - id of the reply
     * @param {string} parentId - id of the parent item
     * @param {string} text - the reply updated text
     * @param {BoxCommentPermission} permissions - permissions associated with the reply
     * @param {Function} onSuccess - the success callback
     * @param {Function} onError - the error callback
     * @return {void}
     */
    _defineProperty(this, "updateReply", (id, parentId, text, permissions, onSuccess, onError) => {
      const {
        api,
        emitAnnotationReplyUpdateEvent,
        file
      } = this.props;
      emitAnnotationReplyUpdateEvent({
        id,
        tagged_message: text
      }, parentId, true);
      api.getFeedAPI(false).updateReply(file, id, parentId, text, permissions, this.updateReplySuccessCallback.bind(this, parentId, onSuccess), (error, code) => {
        if (onError) {
          onError(error, code);
        }
        this.feedErrorCallback(error, code);
      });

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Updates replies of a comment or annotation in the Feed
     *
     * @param {string} id - id of the feed item
     * @param {Array<Comment>} replies - replies
     * @return {void}
     */
    _defineProperty(this, "updateReplies", (id, replies) => {
      const {
        activeFeedEntryId,
        api,
        file,
        history,
        internalSidebarNavigationHandler,
        routerDisabled
      } = this.props;
      const {
        feedItems
      } = this.state;
      if (!feedItems) {
        return;
      }
      const feedAPI = api.getFeedAPI(false);
      feedAPI.file = file;

      // Detect if replies are being hidden and activeFeedEntryId belongs to a reply
      // that is in currently being updated parent, in order to disable active item
      if (activeFeedEntryId && replies.length === 1 && feedItems.some(item => item.id === id && item === this.getCommentFeedItemByReplyId(feedItems, activeFeedEntryId))) {
        if (routerDisabled && internalSidebarNavigationHandler) {
          internalSidebarNavigationHandler({
            sidebar: ViewType.ACTIVITY
          }, true);
        } else {
          history.replace(this.getActiveCommentPath());
        }
      }
      feedAPI.updateFeedItem({
        replies
      }, id);
      this.fetchFeedItems();
    });
    /**
     * Handles a successful update of a reply
     *
     * @private
     * @param {string} parentId - The id of the reply's parent item
     * @param {Function} onSuccess - the success callback
     * @param {Comment} reply - The reply comment object
     * @return {void}
     */
    _defineProperty(this, "updateReplySuccessCallback", (parentId, onSuccess, reply) => {
      const {
        emitAnnotationReplyUpdateEvent
      } = this.props;
      this.feedSuccessCallback();
      emitAnnotationReplyUpdateEvent(reply, parentId);
      if (onSuccess) {
        onSuccess();
      }
    });
    /**
     * Posts a new comment to the API
     *
     * @param {string} text - The comment's text
     * @param {boolean} hasMention - The comment's text
     * @return {void}
     */
    _defineProperty(this, "createComment", (text, hasMention) => {
      const {
        api,
        currentUser,
        features,
        file,
        hasReplies,
        onCommentCreate
      } = this.props;
      const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled');
      if (!currentUser) {
        throw getBadUserError();
      }
      const successCallback = comment => {
        onCommentCreate(comment);
        this.feedSuccessCallback();
      };
      if (hasReplies || isThreadedRepliesV2Enabled) {
        api.getFeedAPI(false).createThreadedComment(file, currentUser, text, successCallback, this.feedErrorCallback);
      } else {
        api.getFeedAPI(false).createComment(file, currentUser, text, hasMention, successCallback, this.feedErrorCallback);
      }

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Posts a new reply to the API
     *
     * @param {string} parentId - The id of the parent item
     * @param {CommentFeedItemType} parentType - The type of the parent item
     * @param {string} text - The text of reply
     * @return {void}
     */
    _defineProperty(this, "createReply", (parentId, parentType, text) => {
      const {
        api,
        currentUser,
        emitAnnotationReplyCreateEvent,
        file
      } = this.props;
      if (!currentUser) {
        throw getBadUserError();
      }
      const eventRequestId = uniqueId('comment_');
      emitAnnotationReplyCreateEvent({
        tagged_message: text
      }, eventRequestId, parentId, true);
      api.getFeedAPI(false).createReply(file, currentUser, parentId, parentType, text, this.createReplySuccessCallback.bind(this, eventRequestId, parentId), this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Handles a successful creation of a reply
     *
     * @private
     * @param {string} eventRequestId - The id of the parent item
     * @param {string} parentId - The id of the reply's parent item
     * @param {Comment} reply - The reply comment object
     * @return {void}
     */
    _defineProperty(this, "createReplySuccessCallback", (eventRequestId, parentId, reply) => {
      const {
        emitAnnotationReplyCreateEvent
      } = this.props;
      this.feedSuccessCallback();
      emitAnnotationReplyCreateEvent(reply, eventRequestId, parentId);
    });
    /**
     * Deletes an app activity item via the API.
     *
     * @param {Object} args - A subset of the app activity
     * @return void
     */
    _defineProperty(this, "deleteAppActivity", ({
      id
    }) => {
      const {
        api,
        file
      } = this.props;
      api.getFeedAPI(false).deleteAppActivity(file, id, this.feedSuccessCallback, this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    _defineProperty(this, "fetchRepliesForFeedItems", feedItems => {
      const {
        activeFeedEntryId
      } = this.props;
      if (!activeFeedEntryId) {
        return;
      }
      this.getActiveFeedEntryData(feedItems).then(({
        id,
        type
      }) => {
        if (!id || !type || this.isActiveEntryInFeed(feedItems, activeFeedEntryId) || !this.isItemTypeComment(type)) {
          return Promise.resolve(feedItems);
        }
        const parentType = type === FEED_ITEM_TYPE_COMMENT ? FEED_ITEM_TYPE_COMMENT : FEED_ITEM_TYPE_ANNOTATION;
        return this.getFeedItemsWithReplies(feedItems, id, parentType);
      }).then(updatedItems => this.fetchFeedItemsSuccessCallback(updatedItems)).catch(error => this.fetchFeedItemsErrorCallback(feedItems, [error]));
    });
    /**
     * Handles a successful feed API fetch
     *
     * @private
     * @param {Array} feedItems - the feed items
     * @return {void}
     */
    _defineProperty(this, "fetchFeedItemsSuccessCallback", feedItems => {
      const {
        file: {
          id: fileId
        },
        logger
      } = this.props;
      mark(MARK_NAME_DATA_READY);

      // Only emit metric if has >1 activity feed items (there should always at least be the current version)
      if (feedItems.length > 1) {
        logger.onDataReadyMetric({
          endMarkName: MARK_NAME_DATA_READY,
          startMarkName: MARK_NAME_DATA_LOADING
        }, fileId);
      }
      this.setState({
        feedItems,
        activityFeedError: undefined
      });
    });
    /**
     * Logs diff between UAA and v2 API data
     *
     * @param {{}[]} responseParity array of aggragated responses from UAA and v2
     * @param {{}} parsedDataParity parsed data from UAA and v2
     * @return {void}
     */
    _defineProperty(this, "logAPIParity", parityData => {
      const {
        logger
      } = this.props;
      logger.onPreviewMetric({
        parityData,
        type: METRIC_TYPE_UAA_PARITY_METRIC
      });
    });
    /**
     * Handles a failed feed item fetch
     *
     * @private
     * @param {Error} e - API error
     * @return {void}
     */
    _defineProperty(this, "fetchFeedItemsErrorCallback", (feedItems, errors) => {
      const {
        onError
      } = this.props;
      this.setState({
        feedItems,
        activityFeedError: activityFeedInlineError
      });
      if (Array.isArray(errors) && errors.length) {
        onError(new Error('Fetch feed items error'), ERROR_CODE_FETCH_ACTIVITY, {
          showNotification: true,
          errors: errors.map(({
            code
          }) => code)
        });
      }
    });
    _defineProperty(this, "getCommentFeedItemWithReplies", (feedItem, replies) => _objectSpread(_objectSpread({}, feedItem), {}, {
      replies
    }));
    _defineProperty(this, "getFeedItemsWithReplies", (feedItems, id, type) => {
      const {
        api,
        file
      } = this.props;
      return new Promise((resolve, reject) => {
        if (!id || !type) {
          resolve(feedItems);
          return;
        }
        api.getFeedAPI(false).fetchReplies(file, id, type, replies => {
          const updatedFeedItems = feedItems.map(item => {
            if (item.id === id && this.isItemTypeComment(item.type)) {
              if (item.type === FEED_ITEM_TYPE_ANNOTATION) {
                return this.getCommentFeedItemWithReplies(item, replies);
              }
              if (item.type === FEED_ITEM_TYPE_COMMENT) {
                return this.getCommentFeedItemWithReplies(item, replies);
              }
            }
            return item;
          });
          resolve(updatedFeedItems);
        }, error => {
          reject(error);
        });
      });
    });
    /**
     * Network error callback
     *
     * @private
     * @param {Error} error - Error object
     * @param {Error} code - the code for the error
     * @param {Object} contextInfo - the context info for the error
     * @return {void}
     */
    _defineProperty(this, "errorCallback", (error, code, contextInfo = {}) => {
      /* eslint-disable no-console */
      console.error(error);
      /* eslint-enable no-console */

      // eslint-disable-next-line react/prop-types
      this.props.onError(error, code, contextInfo);
    });
    /**
     * File approver contacts fetch success callback
     *
     * @private
     * @param {BoxItemCollection} collaborators - Collaborators response data
     * @return {void}
     */
    _defineProperty(this, "getApproverContactsSuccessCallback", collaborators => {
      const {
        entries
      } = collaborators;
      this.setState({
        approverSelectorContacts: entries
      });
    });
    /**
     * File @mention contacts fetch success callback
     *
     * @private
     * @param {BoxItemCollection} collaborators - Collaborators response data
     * @return {void}
     */
    _defineProperty(this, "getMentionContactsSuccessCallback", collaborators => {
      const {
        entries
      } = collaborators;
      this.setState({
        contactsLoaded: false
      }, () => this.setState({
        contactsLoaded: true,
        mentionSelectorContacts: entries
      }));
    });
    /**
     * Fetches file @mention's with groups
     *
     * @private
     * @param {string} searchStr - Search string to filter file collaborators by
     * @return {void}
     */
    _defineProperty(this, "getApprover", debounce(searchStr => {
      const {
        file,
        api
      } = this.props;
      api.getFileCollaboratorsAPI(false).getCollaboratorsWithQuery(file.id, this.getApproverContactsSuccessCallback, this.errorCallback, searchStr, {
        includeGroups: true,
        respectHiddenCollabs: true
      });
    }, DEFAULT_COLLAB_DEBOUNCE));
    /**
     * Fetches file @mention's
     *
     * @private
     * @param {string} searchStr - Search string to filter file collaborators by
     * @return {void}
     */
    _defineProperty(this, "getMention", debounce(searchStr => {
      const {
        file,
        api
      } = this.props;
      api.getFileCollaboratorsAPI(false).getCollaboratorsWithQuery(file.id, this.getMentionContactsSuccessCallback, this.errorCallback, searchStr);
    }, DEFAULT_COLLAB_DEBOUNCE));
    _defineProperty(this, "pendingMentionResolve", null);
    _defineProperty(this, "pendingMentionReject", null);
    _defineProperty(this, "mentionGeneration", 0);
    // Debounced inner fetch. The generation arg guards against in-flight
    // responses from superseded requests resolving/rejecting the current
    // promise with stale data.
    _defineProperty(this, "debouncedFetchMentionCollaborators", debounce((searchStr, generation) => {
      const {
        api,
        file
      } = this.props;
      api.getFileCollaboratorsAPI(false).getCollaboratorsWithQuery(file.id, collaborators => {
        if (generation === this.mentionGeneration && this.pendingMentionResolve) {
          this.pendingMentionResolve(collaborators.entries);
          this.pendingMentionResolve = null;
          this.pendingMentionReject = null;
        }
      }, (error, code, contextInfo) => {
        if (generation !== this.mentionGeneration) {
          return;
        }
        this.errorCallback(error, code, contextInfo);
        if (this.pendingMentionReject) {
          this.pendingMentionReject(error);
          this.pendingMentionResolve = null;
          this.pendingMentionReject = null;
        }
      }, searchStr);
    }, DEFAULT_COLLAB_DEBOUNCE));
    _defineProperty(this, "getMentionAsync", searchStr => {
      if (this.pendingMentionResolve) {
        this.pendingMentionResolve([]);
      }
      this.mentionGeneration += 1;
      const generation = this.mentionGeneration;
      return new Promise((resolve, reject) => {
        this.pendingMentionResolve = resolve;
        this.pendingMentionReject = reject;
        this.debouncedFetchMentionCollaborators(searchStr, generation);
      });
    });
    /**
     * Returns feed item based on the item id
     *
     * @param {FeedItems} feedItems - the feed items
     * @param {string} itemId - feed item id
     * @return {FeedItem | undefined}
     */
    _defineProperty(this, "getFocusableFeedItemById", (feedItems, itemId) => {
      if (!itemId) {
        return undefined;
      }
      return feedItems.find(({
        id,
        type
      }) => id === itemId && this.isItemTypeFocusable(type));
    });
    /**
     * Returns parent feed item based on the reply id
     *
     * @param {FeedItems} feedItems - the feed items
     * @param {string} replyId - feed item's reply id
     * @return {FeedItem | undefined}
     */
    _defineProperty(this, "getCommentFeedItemByReplyId", (feedItems, replyId) => {
      if (!replyId) {
        return undefined;
      }
      return feedItems.find(item => {
        if (item.type !== FEED_ITEM_TYPE_ANNOTATION && item.type !== FEED_ITEM_TYPE_COMMENT || !item.replies) {
          return false;
        }
        return item.replies.some(({
          id
        }) => id === replyId);
      });
    });
    /**
     * Returns true if item (based on given item id) is found within feed items or its replies and it, or its parent, can be active (focusable)
     *
     * @param {FeedItems} feedItems - the feed items
     * @param {string} itemId - feed item id
     * @return {boolean}
     */
    _defineProperty(this, "isActiveEntryInFeed", (feedItems, itemId) => !!(this.getFocusableFeedItemById(feedItems, itemId) || this.getCommentFeedItemByReplyId(feedItems, itemId)));
    _defineProperty(this, "isItemTypeFocusable", type => type === FEED_ITEM_TYPE_ANNOTATION || type === FEED_ITEM_TYPE_COMMENT || type === FEED_ITEM_TYPE_TASK);
    _defineProperty(this, "isItemTypeComment", type => type === FEED_ITEM_TYPE_ANNOTATION || type === FEED_ITEM_TYPE_COMMENT);
    /**
     * Returns active entry data (id, type) based on the activeFeedEntryId and activeFeedEntryType values
     * (it can be existing item or parent if the active entry id belongs to a reply)
     *
     * @param {FeedItems} feedItems - the feed items
     * @return {Promise<{ id: string, type?: FocusableFeedItemType }>}
     */
    _defineProperty(this, "getActiveFeedEntryData", feedItems => {
      const {
        activeFeedEntryId,
        activeFeedEntryType,
        api,
        file
      } = this.props;
      return new Promise((resolve, reject) => {
        if (!activeFeedEntryId || !activeFeedEntryType || !this.isItemTypeFocusable(activeFeedEntryType)) {
          resolve({});
          return;
        }

        // Check if the active entry is a first level Feed item
        const firstLevelItem = this.getFocusableFeedItemById(feedItems, activeFeedEntryId);
        if (firstLevelItem) {
          const {
            id,
            type
          } = firstLevelItem;
          resolve({
            id,
            type
          });
          return;
        }

        // Check if the active entry is within replies of any first level Feed items
        const firstLevelItemWithActiveReply = this.getCommentFeedItemByReplyId(feedItems, activeFeedEntryId);
        if (firstLevelItemWithActiveReply) {
          const {
            id,
            type
          } = firstLevelItemWithActiveReply;
          resolve({
            id,
            type
          });
          return;
        }

        // If the active entry could not be found within feed items, it's most likely a reply that
        // is not yet visible in feed and we need to fetch its data in order to find parent
        api.getFeedAPI(false).fetchThreadedComment(file, activeFeedEntryId, ({
          parent
        }) => {
          const parentItem = this.getFocusableFeedItemById(feedItems, parent?.id);
          const {
            id,
            type
          } = parentItem || {};
          resolve({
            id,
            type
          });
        }, error => {
          if (error.status === 404) {
            resolve({});
          } else {
            reject(error);
          }
        });
      });
    });
    /**
     * Fetches replies (comments) of a comment or annotation
     *
     * @param {string} id - id of the feed item
     * @param {CommentFeedItemType} type - type of the feed item
     * @return {void}
     */
    _defineProperty(this, "getReplies", (id, type) => {
      const {
        api,
        file
      } = this.props;
      api.getFeedAPI(false).fetchReplies(file, id, type, this.feedSuccessCallback, this.feedErrorCallback);

      // need to load the pending item
      this.fetchFeedItems();
    });
    /**
     * Gets the user avatar URL
     *
     * @param {string} userId the user id
     * @param {string} fileId the file id
     * @return the user avatar URL string for a given user with access token attached
     */
    _defineProperty(this, "getAvatarUrl", async userId => {
      const {
        file,
        api
      } = this.props;
      return api.getUsersAPI(false).getAvatarUrlWithAccessToken(userId, file.id);
    });
    _defineProperty(this, "handleAnnotationSelect", annotation => {
      const {
        file_version,
        id: nextActiveAnnotationId
      } = annotation;
      const {
        emitActiveAnnotationChangeEvent,
        file,
        getAnnotationsMatchPath,
        getAnnotationsPath,
        history,
        internalSidebarNavigation,
        internalSidebarNavigationHandler,
        location,
        onAnnotationSelect,
        routerDisabled
      } = this.props;
      const annotationFileVersionId = getProp(file_version, 'id');
      const currentFileVersionId = getProp(file, 'file_version.id');
      let selectedFileVersionId = currentFileVersionId;
      if (routerDisabled && internalSidebarNavigation) {
        selectedFileVersionId = getProp(internalSidebarNavigation, 'fileVersionId', currentFileVersionId);
      } else {
        const match = getAnnotationsMatchPath(location);
        selectedFileVersionId = getProp(match, 'params.fileVersionId', currentFileVersionId);
      }
      emitActiveAnnotationChangeEvent(nextActiveAnnotationId);
      if (annotationFileVersionId && annotationFileVersionId !== selectedFileVersionId) {
        if (routerDisabled && internalSidebarNavigationHandler) {
          internalSidebarNavigationHandler({
            sidebar: ViewType.ACTIVITY,
            activeFeedEntryId: nextActiveAnnotationId,
            activeFeedEntryType: FeedEntryType.ANNOTATIONS,
            fileVersionId: annotationFileVersionId
          });
        } else {
          history.push(getAnnotationsPath(annotationFileVersionId, nextActiveAnnotationId));
        }
        const deferScrollToOnload = annotation?.target?.location?.type === 'frame';
        onAnnotationSelect(annotation, deferScrollToOnload);
      } else {
        onAnnotationSelect(annotation);
      }
    });
    _defineProperty(this, "handleItemsFiltered", status => {
      const {
        onFilterChange
      } = this.props;
      this.setState({
        feedItemsStatusFilter: status
      });
      onFilterChange(status);
    });
    _defineProperty(this, "getFilteredFeedItems", () => {
      const {
        feedItems,
        feedItemsStatusFilter
      } = this.state;
      if (!feedItems || !feedItemsStatusFilter || feedItemsStatusFilter === ACTIVITY_FILTER_OPTION_ALL) {
        return feedItems;
      }
      // Filter is completed on two properties (status and type) because filtering on comments (resolved vs. unresolved)
      // requires looking at item status to see if it is open or resolved. To filter all tasks, we need to look at the
      // item type. Item type is also used to keep versions in the feed. Task also has a status but it's status will be
      // "NOT_STARTED" or "COMPLETED" so it will not conflict with comment's status.
      return feedItems.filter(item => {
        return item.status === feedItemsStatusFilter || item.type === FEED_ITEM_TYPE_VERSION || item.type === feedItemsStatusFilter;
      });
    });
    _defineProperty(this, "onTaskModalClose", () => {
      this.setState({
        approverSelectorContacts: []
      });
    });
    _defineProperty(this, "renderAddTaskButton", () => {
      const {
        isDisabled,
        hasTasks,
        internalSidebarNavigation,
        internalSidebarNavigationHandler,
        routerDisabled
      } = this.props;
      const {
        approverSelectorContacts
      } = this.state;
      const {
        getApprover,
        getAvatarUrl,
        createTask,
        onTaskModalClose
      } = this;
      if (!hasTasks) {
        return null;
      }
      return /*#__PURE__*/React.createElement(AddTaskButton, {
        internalSidebarNavigation: internalSidebarNavigation,
        internalSidebarNavigationHandler: internalSidebarNavigationHandler,
        isDisabled: isDisabled,
        onTaskModalClose: onTaskModalClose,
        routerDisabled: routerDisabled,
        taskFormProps: {
          approvers: [],
          approverSelectorContacts,
          completionRule: TASK_COMPLETION_RULE_ALL,
          createTask,
          getApproverWithQuery: getApprover,
          getAvatarUrl,
          id: '',
          message: ''
        }
      });
    });
    _defineProperty(this, "renderActivitySidebarFilter", () => {
      const {
        features,
        hasTasks
      } = this.props;
      const {
        feedItemsStatusFilter
      } = this.state;
      const shouldShowActivityFeedFilter = isFeatureEnabled(features, 'activityFeed.filter.enabled');
      const shouldShowAdditionalFilterOptions = isFeatureEnabled(features, 'activityFeed.newThreadedReplies.enabled');
      if (!shouldShowActivityFeedFilter) {
        return null;
      }
      const activityFilterOptions = [ACTIVITY_FILTER_OPTION_ALL, ACTIVITY_FILTER_OPTION_UNRESOLVED];
      if (shouldShowAdditionalFilterOptions) {
        // Determine which filter options to show based on what activity types are available in current context
        activityFilterOptions.push(ACTIVITY_FILTER_OPTION_RESOLVED);
        if (hasTasks) {
          activityFilterOptions.push(ACTIVITY_FILTER_OPTION_TASKS);
        }
      }
      return /*#__PURE__*/React.createElement(ActivitySidebarFilter, {
        activityFilterOptions: activityFilterOptions,
        feedItemType: feedItemsStatusFilter,
        onFeedItemTypeClick: selectedStatus => {
          this.handleItemsFiltered(selectedStatus);
        }
      });
    });
    _defineProperty(this, "renderActions", () => /*#__PURE__*/React.createElement(React.Fragment, null, this.renderActivitySidebarFilter(), this.renderAddTaskButton()));
    _defineProperty(this, "renderTitle", () => {
      const {
        features
      } = this.props;
      const shouldHideTitle = isFeatureEnabled(features, 'activityFeed.filter.enabled');
      if (shouldHideTitle) {
        return null;
      }
      return /*#__PURE__*/React.createElement(FormattedMessage, messages.sidebarActivityTitle);
    });
    const {
      logger: _logger
    } = this.props;
    mark(MARK_NAME_DATA_LOADING);
    _logger.onReadyMetric({
      endMarkName: MARK_NAME_JS_READY
    });
    this.state = {};
  }
  componentDidMount() {
    const {
      shouldFetchSidebarData = true
    } = this.props;
    if (shouldFetchSidebarData) {
      this.fetchFeedItems(true);
    }
  }
  componentDidUpdate(prevProps) {
    const {
      shouldFetchSidebarData = true
    } = this.props;
    const {
      shouldFetchSidebarData: prevShouldFetchSidebarData = true
    } = prevProps;

    // Fetch when fetch is enabled
    if (!prevShouldFetchSidebarData && shouldFetchSidebarData) {
      this.fetchFeedItems(true);
    }
  }
  componentWillUnmount() {
    // Cancel pending debounces and bump the generation so any in-flight
    // response from getCollaboratorsWithQuery is ignored after unmount.
    [this.getMention, this.debouncedFetchMentionCollaborators].forEach(fn => {
      if (typeof fn?.cancel === 'function') fn.cancel();
    });
    this.mentionGeneration += 1;
    // Resolve any pending getMentionAsync promise with an empty result so
    // awaiters (e.g. ActivityFeedV2's fetchUsers) don't hang forever.
    if (this.pendingMentionResolve) {
      this.pendingMentionResolve([]);
    }
    this.pendingMentionResolve = null;
    this.pendingMentionReject = null;
  }
  deleteAnnotationSuccess(id) {
    const {
      emitAnnotationRemoveEvent
    } = this.props;
    this.feedSuccessCallback();
    emitAnnotationRemoveEvent(id);
  }
  /**
   * Fetches the feed items for the sidebar
   *
   * @param {boolean} shouldRefreshCache true if the cache should be refreshed
   * @param {boolean} shouldDestroy true if the api factory should be destroyed
   */
  fetchFeedItems(shouldRefreshCache = false, shouldDestroy = false) {
    const {
      activeFeedEntryId,
      activeFeedEntryType,
      api,
      file,
      features,
      hasReplies,
      hasTasks: shouldShowTasks,
      hasVersions: shouldShowVersions
    } = this.props;
    const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled');
    const shouldShowReplies = hasReplies || isThreadedRepliesV2Enabled;
    const shouldFetchReplies = shouldRefreshCache && hasReplies && activeFeedEntryId && activeFeedEntryType === FEED_ITEM_TYPE_COMMENT;
    const shouldShowAppActivity = isFeatureEnabled(features, 'activityFeed.appActivity.enabled');
    const shouldShowAnnotations = isFeatureEnabled(features, 'activityFeed.annotations.enabled');
    const shouldUseUAA = isFeatureEnabled(features, 'activityFeed.uaaIntegration.enabled');
    api.getFeedAPI(shouldDestroy).feedItems(file, shouldRefreshCache, shouldFetchReplies ? this.fetchRepliesForFeedItems : this.fetchFeedItemsSuccessCallback, this.fetchFeedItemsErrorCallback, this.errorCallback, {
      shouldShowAnnotations,
      shouldShowAppActivity,
      shouldShowReplies,
      shouldShowTasks,
      shouldShowVersions,
      shouldUseUAA,
      useEnhancedActivities: isThreadedRepliesV2Enabled
    }, shouldUseUAA ? this.logAPIParity : undefined);
  }
  getActiveCommentPath(commentId) {
    if (!commentId) {
      return '/activity';
    }
    return generatePath('/:sidebar/comments/:commentId?', {
      sidebar: 'activity',
      commentId
    });
  }
  refresh(shouldRefreshCache = true) {
    this.fetchFeedItems(shouldRefreshCache);
  }
  render() {
    const {
      activeFeedEntryId,
      activeFeedEntryType,
      currentUser,
      currentUserError,
      elementId,
      features,
      file,
      hasReplies,
      hasVersions,
      isDisabled = false,
      onAnnotationCopyLink,
      onCommentCopyLink,
      onShowOnlyMentionsMeChange,
      onShowResolvedChange,
      onVersionHistoryClick,
      getUserProfileUrl,
      onTaskView,
      showOnlyMentionsMe,
      showResolved
    } = this.props;
    const {
      activityFeedError,
      approverSelectorContacts,
      contactsLoaded,
      mentionSelectorContacts
    } = this.state;
    const isNewThreadedRepliesEnabled = isFeatureEnabled(features, 'activityFeed.newThreadedReplies.enabled');
    const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled');
    const shouldUseUAA = isFeatureEnabled(features, 'activityFeed.uaaIntegration.enabled');
    if (isThreadedRepliesV2Enabled) {
      const label = `${elementId}${elementId === '' ? '' : '_'}${SIDEBAR_VIEW_ACTIVITY}`;
      return /*#__PURE__*/React.createElement("div", {
        "aria-labelledby": label,
        className: "bcs-content",
        "data-testid": "bcs-content",
        id: `${label}-content`,
        role: "tabpanel"
      }, /*#__PURE__*/React.createElement(ActivityFeedV2, {
        activeFeedEntryId: activeFeedEntryId,
        approverSelectorContacts: approverSelectorContacts,
        createTask: this.createTask,
        currentUser: currentUser,
        feedItems: this.getFilteredFeedItems(),
        getApproverWithQuery: this.getApprover,
        getAvatarUrl: this.getAvatarUrl,
        getMentionAsync: this.getMentionAsync,
        getTaskCollaborators: this.getTaskCollaborators,
        hasTasks: this.props.hasTasks,
        isDisabled: isDisabled,
        onAnnotationCopyLink: onAnnotationCopyLink,
        onAnnotationDelete: this.handleAnnotationDelete,
        onAnnotationEdit: this.handleAnnotationEdit,
        onAnnotationSelect: this.handleAnnotationSelect,
        onAnnotationStatusChange: this.handleAnnotationStatusChange,
        onCommentCopyLink: onCommentCopyLink,
        onCommentCreate: this.createComment,
        onCommentDelete: this.deleteComment,
        onCommentUpdate: this.updateComment,
        onReplyCreate: this.createReply,
        onReplyDelete: this.deleteReply,
        onReplyUpdate: ({
          id,
          onError,
          onSuccess,
          parentId,
          permissions,
          text
        }) => this.updateReply(id, parentId, text, permissions, onSuccess, onError),
        onShowOnlyMentionsMeChange: onShowOnlyMentionsMeChange,
        onShowResolvedChange: onShowResolvedChange,
        onTaskAssignmentUpdate: this.updateTaskAssignment,
        onTaskDelete: this.deleteTask,
        onTaskUpdate: this.updateTask,
        onTaskView: onTaskView,
        onVersionHistoryClick: onVersionHistoryClick,
        showOnlyMentionsMe: showOnlyMentionsMe,
        showResolved: showResolved
      }));
    }
    return /*#__PURE__*/React.createElement(SidebarContent, {
      actions: this.renderActions(),
      className: classNames('bcs-activity', {
        'bcs-activity--full': hasReplies
      }),
      elementId: elementId,
      sidebarView: SIDEBAR_VIEW_ACTIVITY,
      title: this.renderTitle()
    }, /*#__PURE__*/React.createElement(ActivityFeed, {
      activeFeedEntryId: activeFeedEntryId,
      activeFeedEntryType: activeFeedEntryType,
      activityFeedError: activityFeedError,
      approverSelectorContacts: approverSelectorContacts,
      currentUser: currentUser,
      currentUserError: currentUserError,
      feedItems: this.getFilteredFeedItems(),
      file: file,
      getApproverWithQuery: this.getApprover,
      getAvatarUrl: this.getAvatarUrl,
      getMentionWithQuery: this.getMention,
      getUserProfileUrl: getUserProfileUrl,
      hasNewThreadedReplies: isNewThreadedRepliesEnabled,
      hasReplies: hasReplies,
      hasVersions: hasVersions,
      isDisabled: isDisabled,
      mentionSelectorContacts: mentionSelectorContacts,
      contactsLoaded: contactsLoaded,
      onAnnotationDelete: this.handleAnnotationDelete,
      onAnnotationEdit: this.handleAnnotationEdit,
      onAnnotationSelect: this.handleAnnotationSelect,
      onAnnotationStatusChange: this.handleAnnotationStatusChange,
      onAppActivityDelete: this.deleteAppActivity,
      onCommentCreate: this.createComment,
      onCommentDelete: this.deleteComment,
      onCommentUpdate: this.updateComment,
      onHideReplies: this.updateReplies,
      onReplyCreate: this.createReply,
      onReplyDelete: this.deleteReply,
      onReplyUpdate: this.updateReply,
      onShowReplies: this.getReplies,
      onTaskAssignmentUpdate: this.updateTaskAssignment,
      onTaskCreate: this.createTask,
      onTaskDelete: this.deleteTask,
      onTaskModalClose: this.onTaskModalClose,
      onTaskUpdate: this.updateTask,
      onTaskView: onTaskView,
      onVersionHistoryClick: onVersionHistoryClick,
      shouldUseUAA: shouldUseUAA
    }));
  }
}
_defineProperty(ActivitySidebar, "defaultProps", {
  annotatorState: {},
  emitActiveAnnotationChangeEvent: noop,
  emitAnnotationRemoveEvent: noop,
  emitAnnotationReplyCreateEvent: noop,
  emitAnnotationReplyDeleteEvent: noop,
  emitAnnotationReplyUpdateEvent: noop,
  emitAnnotationUpdateEvent: noop,
  getAnnotationsMatchPath: noop,
  getAnnotationsPath: noop,
  hasReplies: false,
  hasTasks: true,
  hasVersions: true,
  isDisabled: false,
  onAnnotationSelect: noop,
  onCommentCreate: noop,
  onCommentDelete: noop,
  onCommentUpdate: noop,
  onFilterChange: noop,
  onTaskAssignmentUpdate: noop,
  onTaskCreate: noop,
  onTaskDelete: noop,
  onTaskUpdate: noop,
  onVersionChange: noop,
  onVersionHistoryClick: noop
});
export { ActivitySidebar as ActivitySidebarComponent };
export default flow([withLogger(ORIGIN_ACTIVITY_SIDEBAR), withErrorBoundary(ORIGIN_ACTIVITY_SIDEBAR), withAPIContext, withFeatureConsumer, withAnnotatorContext, withRouterAndRef])(ActivitySidebar);
//# sourceMappingURL=ActivitySidebar.js.map