UNPKG

@atlaskit/mention

Version:

A React component used to display user profiles in a list for 'Mention' functionality

192 lines (184 loc) 6.96 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import { SLI_EVENT_TYPE } from './../util/analytics'; import { utils as serviceUtils } from '@atlaskit/util-service-support'; import { UserType, UserAccessLevel, SliNames, Actions } from '../types'; import MentionResource from './MentionResource'; import debug from '../util/logger'; const MAX_QUERY_TEAMS = 20; /** * Provides a Javascript API to fetch users and teams * In future we will have a new endpoint to return both users and teams, we can * remove this class at this point */ export default class TeamMentionResource extends MentionResource { constructor(userMentionConfig, teamMentionConfig) { super(userMentionConfig); _defineProperty(this, "lastSearchQuery", ''); this.verifyMentionConfig(teamMentionConfig); this.teamMentionConfig = teamMentionConfig; this.lastReturnedSearchTeam = 0; } filter(query, contextIdentifier) { this.lastSearchQuery = query; if (!query) { return this.remoteInitialStateTeamAndUsers(contextIdentifier); } else { this.updateActiveSearches(query); // both user and team requests start at the same time const getUserPromise = this.remoteUserSearch(query, contextIdentifier); const getTeamsPromise = this.remoteTeamSearch(query, contextIdentifier); return this.handleBothRequests(query, getUserPromise, getTeamsPromise); } } /** * Returns the initial mention display list before a search is performed for the specified * container. */ async remoteInitialStateTeamAndUsers(contextIdentifier) { const emptyQuery = ''; const getUserPromise = super.remoteInitialState(contextIdentifier); const queryParams = this.getQueryParamsOfTeamMentionConfig(contextIdentifier); const options = { path: 'bootstrap', queryParams }; const getTeamsPromise = serviceUtils.requestService(this.teamMentionConfig, options); this.handleBothRequests(emptyQuery, getUserPromise, getTeamsPromise); } /** * Both user and team requests are not blocked together * If users request arrives first, show users. Show teams when team request arrives. * If team request arrives first, block waiting for user request, then show both * If one errors, show the non-erroring one * If both error, show error */ async handleBothRequests(query, userRequest, teamRequest) { const searchTime = Date.now(); let accumulatedResults = { mentions: [], query }; const notifyWhenOneRequestDone = (results, hasTeamResults) => { // just update UI for the last query string if (query !== this.lastSearchQuery) { return; } accumulatedResults = { mentions: [...accumulatedResults.mentions, ...results.mentions], query }; // we need to calculate different `duration` for user and team request. if (hasTeamResults) { this.notify(searchTime, accumulatedResults, query); } else { super.notify(searchTime, accumulatedResults, query); } }; let userResults; let userRequestError = null; let teamRequestError = null; try { // user requests finishes, update the UI, don't need to wait for team requests userResults = await userRequest; notifyWhenOneRequestDone(userResults, false); } catch (error) { userRequestError = error; } // team request will wait for user request done try { const teamsResult = await teamRequest; // update search time after team results returns notifyWhenOneRequestDone(Array.isArray(teamsResult) ? this.convertTeamResultToMentionResult(teamsResult, query) : teamsResult, true); } catch (error) { teamRequestError = error; } // both requests fail, show one of errors in UI if (userRequestError && teamRequestError) { this.notifyError(userRequestError, query); debug('User mention request fails. ', userRequestError); debug('Team mention request fails. ', teamRequestError); } } notify(searchTime, mentionResult, query) { if (searchTime > this.lastReturnedSearchTeam) { this.lastReturnedSearchTeam = searchTime; this._notifyListeners(mentionResult, { teamMentionDuration: Date.now() - searchTime }); } else { const date = new Date(searchTime).toISOString().substr(17, 6); debug('Stale search result, skipping', date, query); // eslint-disable-line no-console, max-len } this._notifyAllResultsListeners(mentionResult); } getQueryParamsOfTeamMentionConfig(contextIdentifier) { const configParams = {}; if (this.teamMentionConfig.containerId) { configParams['containerId'] = this.teamMentionConfig.containerId; } if (this.teamMentionConfig.productId) { configParams['productIdentifier'] = this.teamMentionConfig.productId; } // if contextParams exist then it will override configParams for containerId return { ...configParams, ...contextIdentifier }; } remoteUserSearch(query, contextIdentifier) { return super.remoteSearch(query, contextIdentifier); } async remoteTeamSearch(query, contextIdentifier) { const options = { path: 'search', queryParams: { query, limit: MAX_QUERY_TEAMS, ...this.getQueryParamsOfTeamMentionConfig(contextIdentifier) } }; try { const teamResult = await serviceUtils.requestService(this.teamMentionConfig, options); this._notifyAnalyticsListeners(SLI_EVENT_TYPE, SliNames.SEARCH_TEAM, Actions.SUCCEEDED); return this.convertTeamResultToMentionResult(teamResult, query); } catch (error) { this._notifyAnalyticsListeners(SLI_EVENT_TYPE, SliNames.SEARCH_TEAM, Actions.FAILED); throw error; } } convertTeamResultToMentionResult(result, query) { const { teamLinkResolver } = this.teamMentionConfig; const mentions = result.map(team => { let teamLink = ''; const defaultTeamLink = `${window.location.origin}/people/team/${team.id}`; if (typeof teamLinkResolver === 'function') { teamLink = teamLinkResolver(team.id); } return { id: this.trimTeamARI(team.id), avatarUrl: team.smallAvatarImageUrl, name: team.displayName, accessLevel: UserAccessLevel[UserAccessLevel.CONTAINER], userType: UserType[UserType.TEAM], highlight: team.highlight, context: { members: team.members, includesYou: team.includesYou, memberCount: team.memberCount, teamLink: teamLink || defaultTeamLink } }; }); return { mentions, query }; } trimTeamARI(teamId = '') { const TEAM_ARI_PREFIX = 'ari:cloud:teams::team/'; const IDENTITY_TEAM_ARI_PREFIX = 'ari:cloud:identity::team/'; return teamId.replace(TEAM_ARI_PREFIX, '').replace(IDENTITY_TEAM_ARI_PREFIX, ''); } }