serge-web/serge-web

View on GitHub
client/src/Helpers/participant-states.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import { CHANNEL_CHAT, CHANNEL_COLLAB, CHANNEL_CUSTOM, CHANNEL_MAPPING, CHAT_MESSAGE_TEMPLATE_ID, PARTICIPANT_CUSTOM } from 'src/config'
import { ChannelCollab, ChannelTypes, ParticipantTemplate, Role, TemplateBody, TemplateBodysByKey } from 'src/custom-types'
import { CoreParticipant, ParticipantCustom } from 'src/custom-types/participant'
import getTemplateById, { getTemplateByIdNoUndefined } from './getTemplateById'
import { matchedForceFilter, matchedV3AllRolesFilter, matchedForceAndRoleFilter } from './participant-filters'

export interface CheckParticipantStates {
  /** whether role is participant in channel */
  isParticipant: boolean
  /** the role filled by the participant */
  templatesIDs: ParticipantTemplate[]
  /** whether all roles for this force are included in the channel */
  allRolesIncluded: boolean
}
export interface ParticipantStates {
  /** whether role is participant in channel */
  isParticipant: boolean
  /** whether player is just channel observer (and not participant) */
  observing: boolean
  /** the templates available to this player in this channel */
  templates: TemplateBody[]
}

// Extracts templates from custom roles within an array of CoreParticipant
const extractTemplatesFromCustomRoles = (participatingRoles: CoreParticipant[]): ParticipantTemplate[] => {
  return participatingRoles.reduce((templates: ParticipantTemplate[], role: CoreParticipant) => {
    if (role.pType === PARTICIPANT_CUSTOM) {
      const theseTemplates = (role as ParticipantCustom).templates
      templates.push(...theseTemplates)
    }
    return templates
  }, [])
}

/** find out if the role is active in the supplied channel
 * Always returns a structure, use isParticipant to determine if role is in channel (registered or as observer)
*/
export const checkV3ParticipantStates = (channel: ChannelTypes, selectedForce: string | undefined, selectedRole: Role['roleId'], isObserver: boolean): CheckParticipantStates => {
  if (selectedForce === undefined) throw new Error('selectedForce is undefined')
  const channelParts: CoreParticipant[] = channel.participants
  const participatingForce: CoreParticipant | undefined = channelParts && channelParts.find(p => matchedForceFilter(p.forceUniqid, selectedForce))
  // not a member of this channel, return false answer
  if (!participatingForce && !isObserver) {
    return {
      isParticipant: false,
      templatesIDs: [],
      allRolesIncluded: false
    }
  }

  const templateIDs: ParticipantTemplate[] = []
  const participatingRoles: CoreParticipant[] = channelParts.filter((p: CoreParticipant) => matchedForceAndRoleFilter(p, selectedForce, selectedRole))
  switch (channel.channelType) {
    case CHANNEL_COLLAB: {
      const collab = channel as ChannelCollab
      if (collab.newMessageTemplate) {
        templateIDs.push(collab.newMessageTemplate)
      }
      break
    }
    case CHANNEL_CHAT:
    case CHANNEL_MAPPING:
      // don't bother adding it
      break
    case CHANNEL_CUSTOM:
      templateIDs.push(...extractTemplatesFromCustomRoles(participatingRoles))
      break
    default:
      console.warn('Encountered unexpected channel type:', channel)
  }

  // is a member of this channel, find out if they're named, or a where all roles for this force are in channel
  return {
    isParticipant: participatingRoles.length > 0,
    templatesIDs: templateIDs,
    allRolesIncluded: !!channelParts.find((p: CoreParticipant) => matchedV3AllRolesFilter(p, selectedForce))
  }
}

export const setTemplatesBasedOnConditions = (
  chosenTemplates: TemplateBody[],
  chatTemplate: TemplateBody | undefined,
  allTemplatesByKey: TemplateBodysByKey
): TemplateBody[] => {
  let templates: TemplateBody[] = []
  if (chosenTemplates.length === 0) {
    if (typeof chatTemplate !== 'undefined') {
      templates = [chatTemplate]
    } else {
      // TODO: remove this workaround
      // for new cloud hosting, all template ids have changed. So,
      // if this happens, just look for template with correct title
      const tempKeys = Object.keys(allTemplatesByKey)
      let chatTemp: TemplateBody | undefined
      tempKeys.some((key: string) => {
        const template = allTemplatesByKey[key]
        if (template.title === 'Chat') {
          chatTemp = template
        }
        // returning non-null value will let us drop out of `some` loop early
        return !!chatTemp
      })
      if (chatTemp) {
        templates = [chatTemp]
      }
    }
  } else {
    templates = chosenTemplates
  }

  return templates
}

export const addTemplate = (template: TemplateBody, chosenTemplates: TemplateBody[], templatesUniqFilter: { [property: string]: boolean }): void => {
  if (templatesUniqFilter[template.title] !== true) {
    templatesUniqFilter[template.title] = true
    chosenTemplates.push(template)
  }
}

export const addTemplatesToChosen = (
  templatesIDs: ParticipantTemplate[] | undefined,
  allTemplatesByKey: TemplateBodysByKey,
  chosenTemplates: TemplateBody[],
  templatesUniqFilter: { [property: string]: boolean }
): void => {
  if (templatesIDs && templatesIDs.length !== 0) {
    for (const template of templatesIDs) {
      addTemplate(
        getTemplateByIdNoUndefined(allTemplatesByKey, template._id),
        chosenTemplates,
        templatesUniqFilter
      )
    }
  } else {
    const chatTemplate = getTemplateById(allTemplatesByKey, CHAT_MESSAGE_TEMPLATE_ID)
    if (typeof chatTemplate !== 'undefined') {
      addTemplate(chatTemplate, chosenTemplates, templatesUniqFilter)
    }
  }
}

/** find out how the user can participate in this channel */
export const getParticipantStates = (
  channel: ChannelTypes, forceId: string | undefined,
  role: Role['roleId'], isObserver: boolean,
  allTemplatesByKey: TemplateBodysByKey,
  // TODO: move it to reducer, for future default Message can be changed from admin
  // k16eedkl - Chat template id
  defaultMessageId = CHAT_MESSAGE_TEMPLATE_ID
): ParticipantStates => {
  const chosenTemplates: TemplateBody[] = []
  let observing = false
  let templates: TemplateBody[] = []
  const templatesUniqFilter: { [property: string]: boolean } = {}

  const {
    isParticipant,
    templatesIDs,
    allRolesIncluded
  }: CheckParticipantStates = checkV3ParticipantStates(channel, forceId, role, isObserver)

  const chatTemplate = getTemplateById(allTemplatesByKey, defaultMessageId)
  if (typeof chatTemplate === 'undefined') {
    // dont't throw error for unit tests, some use legacy data
    const jestWorkerId = process.env.JEST_WORKER_ID
    const inProduction = !jestWorkerId
    inProduction && console.warn('Warning, unable to find Chat template for channel with no templates defined')
    // end
  }
  if (isParticipant) {
    addTemplatesToChosen(templatesIDs, allTemplatesByKey, chosenTemplates, templatesUniqFilter)
  }

  if (isParticipant || allRolesIncluded) {
    templates = setTemplatesBasedOnConditions(chosenTemplates, chatTemplate, allTemplatesByKey)
  }
  
  if (isObserver && !isParticipant && !allRolesIncluded) {
    observing = true
    templates = []
  }
  
  return { isParticipant, observing, templates }
}