serge-web/serge-web

View on GitHub
client/src/ActionsAndReducers/playerUi/helpers/handleWargameMessagesChange.ts

Summary

Maintainability
D
2 days
Test Coverage
import {
  MessageChannel,
  PlayerUi,
  ChannelUI,
  MessageCustom,
  MessageInfoType,
  SetWargameMessage,
  Wargame,
  ChatMessage,
  PlayerMessageLog
} from 'src/custom-types'
import {
  handleChannelUpdates, handleAllInitialChannelMessages, setMessageState, 
  getMessageState, removeMessageState, handleNewMessageData 
} from 'src/Helpers'
import {
  INFO_MESSAGE_CLIPPED
} from 'src/config'
/** a new document has been received, either add it to the correct channel,
 * or update the channels to reflect the new channel definitions
 */
const handleWargameUpdate = (payload: Wargame, newState: PlayerUi): SetWargameMessage => {
  // TODO: only one of `payload` or `newState` will have been received. We should have 
  // two different handlers, one for each change.
  if (!payload._id) {
    console.error('Payload missing id field')
  }
  const res: SetWargameMessage = handleChannelUpdates(payload.data.channels.channels, payload._id || 'note: id missing', payload.gameTurn, newState.channels, newState.chatChannel,
    newState.selectedForce, newState.selectedRole, newState.isObserver,
    newState.allTemplatesByKey, newState.allForces, newState.playerMessageLog)
  return res
}

/** a new document has been received, either add it to the correct channel,
 * or update the channels to reflect the new channel definitions
 */
const handleNewMessage = (payload: MessageChannel, newState: PlayerUi): SetWargameMessage => {
  // TODO: only one of `payload` or `newState` will have been received. We should have 
  // two different handlers, one for each change.
  const res: SetWargameMessage = handleNewMessageData(payload, newState.channels, newState.chatChannel,
    newState.playerMessageLog, newState.selectedRole)
  return res
}

/** when the app first opens it processes a list of all existing messages,,
 * grouping them into channels
 */
const handleSetAllMessages = (payload: Array<MessageCustom | MessageInfoType>, newState: PlayerUi): SetWargameMessage => {
  const res: SetWargameMessage = handleAllInitialChannelMessages(payload, newState.currentWargame, newState.selectedForce,
    newState.selectedRole, newState.allChannels, newState.allForces, newState.chatChannel,
    newState.isObserver, newState.allTemplatesByKey, newState.isUmpire)
  return res
}

const openMessageChange = (message: MessageChannel, id: string): { message: MessageChannel, changed: boolean } => {
  let changed = false
  if (message._id === id) {
    changed = true
    message.isOpen = true
    message.hasBeenRead = true
  }
  return { message, changed }
}

/**
 * Open a message in the specified channel.
 * Mutates the `messages` array.
 */
export const openMessage = (channel: string, payloadMessage: MessageChannel, newState: PlayerUi): ChannelUI => {
  // mutating `messages` array - copyState at top of switch
  const channelMessages: Array<MessageChannel> = (newState.channels[channel].messages || [])
  const selectedForce = newState.selectedForce ? newState.selectedForce.uniqid : ''
  if (payloadMessage._id !== undefined) {
    for (const i in channelMessages) {
      const res = openMessageChange(channelMessages[i], payloadMessage._id)
      if (res.changed) {
        channelMessages[i] = res.message
        setMessageState(newState.currentWargame, selectedForce, newState.selectedRole, payloadMessage._id)
        break
      }
    }
  }

  const unreadMessageCount = channelMessages.filter((message) => {
    return message._id &&
      !message.hasBeenRead &&
      message.messageType !== INFO_MESSAGE_CLIPPED &&
      getMessageState(newState.currentWargame, selectedForce, newState.selectedRole, message._id) === null
  }).length

  return {
    ...newState.channels[channel],
    unreadMessageCount,
    messages: channelMessages
  }
}

const closeMessageChange = (message: MessageChannel, id: string): { message: MessageChannel, changed: boolean } => {
  const changed = false
  if (message.messageType === INFO_MESSAGE_CLIPPED /* InfoType have no id */ && message._id === id) {
    message.isOpen = false
  }
  return { message, changed }
}

export const markUnread = (channel: string, message: MessageChannel | ChatMessage, newState: PlayerUi) => {
  if (!message._id) {
    return {
      ...newState.channels[channel],
      message
    }
  }

  const selectedForce = newState.selectedForce ? newState.selectedForce.uniqid : ''
  removeMessageState(newState.currentWargame, selectedForce, newState.selectedRole, message._id)

  const channelMessages: Array<MessageChannel> = (newState.channels[channel].messages || [])
  const unreadMessageCount = channelMessages.filter((message) => {
    return message._id &&
      !message.hasBeenRead &&
      message.messageType !== INFO_MESSAGE_CLIPPED &&
      getMessageState(newState.currentWargame, selectedForce, newState.selectedRole, message._id) === null
  }).length

  return {
    ...newState.channels[channel],
    unreadMessageCount,
    messages: channelMessages
  }
}

export const closeMessage = (channel: string, payloadMessage: MessageChannel, newState: PlayerUi): (MessageChannel)[] => {
  // mutating messages array - copyState at top of switch
  const channelMessages: Array<MessageChannel> = (newState.channels[channel].messages || [])
  if (payloadMessage._id !== undefined) {
    for (const i in channelMessages) {
      const res = closeMessageChange(channelMessages[i], payloadMessage._id)
      if (res.changed) {
        channelMessages[i] = res.message
        break
      }
    }
  }

  return channelMessages
}

const MarkAllPlayerMessageRead = (newStates: PlayerUi, msgState: string): PlayerMessageLog => {
  Object.values(newStates.playerMessageLog).map((value) => {
    const selectedForce = newStates.selectedForce ? newStates.selectedForce.uniqid : ''
    if (value._id) {
      value.hasBeenRead = msgState === 'read'
      const msgFnc = msgState === 'read' ? setMessageState : removeMessageState
      msgFnc(newStates.currentWargame, selectedForce, newStates.selectedRole, value._id)
    }  
    return value  
  })
  return newStates.playerMessageLog
}

const markAllMessageState = (channel: string, newState: PlayerUi, msgState: 'read' | 'unread'): ChannelUI => {
  const channelMessages: MessageChannel[] = (newState.channels[channel].messages || []).map((message) => {
    const selectedForce = newState.selectedForce ? newState.selectedForce.uniqid : ''
    if (message._id) {
      message.hasBeenRead = msgState === 'read'
      const msgFnc = msgState === 'read' ? setMessageState : removeMessageState
      msgFnc(newState.currentWargame, selectedForce, newState.selectedRole, message._id)
    }
    return message
  })

  return {
    ...newState.channels[channel],
    unreadMessageCount: msgState === 'read' ? 0 : channelMessages.length,
    messages: channelMessages
  }
}

/** 
 * Handle the latest wargame message based on its action payload.
 */
export const handleLatestWargameMessage = (actionPayload: MessageChannel, newState: PlayerUi): PlayerUi => {
  const anyPayload = actionPayload as any
  if (anyPayload.activityTime) {
    return newState
  } else if (anyPayload.data) {
    // wargame change
    const wargame = anyPayload as Wargame
    newState.allChannels = wargame.data.channels.channels
    const changedLatestState = handleWargameUpdate(wargame, newState)
    newState.channels = changedLatestState.channels
    newState.chatChannel = changedLatestState.chatChannel
    newState.playerMessageLog = changedLatestState.playerMessageLog
  } else {
    // process new message
    const changedLatestState = handleNewMessage(actionPayload, newState)
    newState.channels = changedLatestState.channels
    newState.chatChannel = changedLatestState.chatChannel
    newState.playerMessageLog = changedLatestState.playerMessageLog
  }
  return newState
}
  
/**
 * Open a message based on the action payload.
*/
export const openMessageAction = (payload: {
    channel: string
    message: MessageChannel
  }, newState: PlayerUi): void => {
  newState.channels[payload.channel] = openMessage(payload.channel, payload.message, newState)
}

/**
 * Handle the action of setting all messages based on the action payload.
*/
export const handleSetAllMessagesAction = (payload: Array<MessageCustom | MessageInfoType>, newState: PlayerUi): void => {
  const changedAllMesagesState = handleSetAllMessages(payload, newState)
  newState.channels = changedAllMesagesState.channels
  newState.chatChannel = changedAllMesagesState.chatChannel
  newState.playerMessageLog = changedAllMesagesState.playerMessageLog
}

/**
 * Mark all messages in a channel as read based on the action payload.
*/
export const markAllMessagesAsRead = (payload: string | undefined, currentState: PlayerUi) => {
  if (!payload) {
    currentState.playerMessageLog = MarkAllPlayerMessageRead(currentState, 'read')
  } else {
    currentState.channels[payload] = markAllMessageState(payload, currentState, 'read')
  }
}

/**
 * Mark all messages in a channel as unread based on the action payload.
*/
export const markAllMessagesAsUnread = (payload: string | undefined, currentState: PlayerUi) => {
  if (!payload) {
    currentState.playerMessageLog = MarkAllPlayerMessageRead(currentState, 'unread')
  } else {
    currentState.channels[payload] = markAllMessageState(payload, currentState, 'unread')
  }
}