import ifPromise from 'onyx-common/ifPromise'
import hasKey from 'onyx-common/hasKey'
import parseEmbedCode from 'onyx-common/parseEmbedCode'
import normalizeCurrency from 'onyx-common/normalizeCurrency'
import isPastDate from 'onyx-common/isPastDate'
import isFutureDate from 'onyx-common/isFutureDate'
import stripHtml from 'onyx-common/stripHtml'
import calculateSecondsBetween from 'onyx-common/calculateSecondsBetween'
import normalizeArray from 'onyx-common/normalizeArray'
import formatCurrency from 'onyx-common/formatCurrency'
import textCompare from 'onyx-common/textCompare'
import isBase64 from 'onyx-common/isBase64'
import cleanBase64 from 'onyx-common/cleanBase64'
import createEmbedCode from 'onyx-common/createEmbedCode'
const ZEventsApiEvent = ({ prototype }) => {
prototype.normalizeInProgressAction = function (ret) {
if (ret.isDigital && ret.isLive && ret.isHost) return 'start_streaming'
if ((ret.isDigital && !ret.isFree && ret.hasTickets) || (ret.isDigital && ret.isFree)) return 'watch'
if (ret.isSoldOut) return 'sold_out'
if (ret.isCurrentlySellingTickets) return 'buy_tickets'
return false
}
prototype.normalizeEventProvider = function (provider) {
return this.normalizeEmbedCode(provider.embed_code, provider.provider_type)
}
prototype.normalizeEmbedCode = function (embedCode, providerType) {
const videoInfo = parseEmbedCode(embedCode, providerType)
return videoInfo
}
prototype.normalizeEventSpeaker = function (_data) {
const data = this.normalizeData(_data)
const ret = {
eventSpeakerJoinId: parseInt(data.description.record_id),
description: data.description.body,
topic: data.topic,
startTime: this.normalizeDate(data.start_time),
endTime: this.normalizeDate(data.end_time),
speaker: {
isKwivrrUser: !!data.is_kwivrr_user,
avatarUrl: data.avatar_url,
id: parseInt(data.id),
firstName: data.firstname,
lastName: data.lastname,
email: data.email
}
}
return ret
}
/**
* @typedef {object} Event
* @property {number} id - event id
* @property {string} title - event title
* @property {string} description - event description
* @property {string} descriptionText - event description (stripped)
* @property {string} emailContent - event email content
* @property {string} emailContentText - emailContent (stripped)
* @property {Date} startDate - when does event start?
* @property {Date} endDate - when does event end?
* @property {Date} archiveDate - when should event fall off home page feed and should ticket sales be disabled?
* @property {Date} publishDate - when should event be available and ticket sales commence?
* @property {string} shopUrl - shopping link for live stream event
* @property {string} learnMoreUrl - external splash page url
* @property {string} eventPageUrl - url for splash page sharing
* @property {string} livestreamPageUrl - url for livestream page sharing
* @property {string} eventImageUrl - image for hero on splash, and thumbs elsewhere
* @property {boolean} hasImage - was an image uploaded, eventImageUrl will always have a default image even if none was provided
* @property {boolean} hasGeneralTicketsRemaining - are general tickets still available?
* @property {number} generalTicketSales - if host, how many general ticket sales have occurred?
* @property {number} generalTicketsSold - if host, how many general tickets have been sold?
* @property {number} generalTicketPrice - general ticket price
* @property {string} generalTicketDescription - general ticket description
* @property {string} generalTicketDescriptionText - generalTicketDescription (stripped)
* @property {number} generalCapacity - total general tickets
* @property {boolean} hasVipTicketsRemaining - are vip tickets are still available?
* @property {number} vipTicketSales - if host, how many vip ticket sales have occurred?
* @property {number} vipTicketsSold - if host, how many vip tickets have been sold?
* @property {number} vipTicketPrice - general ticket price
* @property {string} vipTicketDescription - vip ticket description
* @property {string} vipTicketDescriptionText - vipTicketDescription (stripped)
* @property {number} vipCapacity - vip ticket description
* @property {number} userTicketsCount - how many tickets does currentUser have?
* @property {boolean} isOnWaitlist - is current user already on the wait list?
* @property {boolean} withCountdownDisplay - should a countdown display on the splash page?
* @property {boolean} isPrivate - is this a private event?
* @property {boolean} isPublic - is this a public event?
* @property {boolean} isPhysical - is this a physical event?
* @property {boolean} isDigital - is this a digital event?
* @property {boolean} isTicketed - is this a ticketed event?
* @property {boolean} isFree - is this a free event?
* @property {boolean} isGeneralTicketed - alias of isTicketed
* @property {boolean} isVipTicketed - is this a vip ticketed event?
* @property {number[]} eventGroupIds - event group ids event is part of
* @property {string[]} availableLanguages - languages available for this event
* @property {string} streamType - if digital event, enum('youtube', 'vimeo', 'kwivrr')
* @property {string} streamUrl - if digital event and not streamType = 'kwivrr', this is the video url to play
* @property {string} promotionalVideoType - service type for promotional video enum('youtube', 'vimeo', 'kwivrr')
* @property {string} promotionalVideoUrl - url for promotional video if provided
* @property {boolean} hasPromotionalVideo - do we have a promo video to attempt to load?
* @property {boolean} hasTicketsRemaining - does this event have tickets of any type remaining?
* @property {number} totalTicketSales - sum of vip and ga ticket sales
* @property {number} totalTicketsSold - sum of vip and ga tickets sold
* @property {boolean} isPublished - is this event published?
* @property {boolean} isArchived - is this event archived?
* @property {boolean} isCurrentlySellingGeneralTickets - are general tickets currently available for purchase?
* @property {boolean} isCurrentlySellingVipTickets - are vip tickets currently available for sale?
* @property {boolean} hasEnded - has the event ended?
* @property {boolean} isPastDate - alias of hasEnded
* @property {boolean} hasStarted - has the event started?
* @property {boolean} isInProgress - is the event currently in progress?
* @property {boolean} isLive - alias of isInProgress
* @property {boolean} isKwivrrStream - is this a red5 stream event?
* @property {boolean} isCurrentlyStreaming - if a digital event, is this currently active?
* @property {boolean} hasEventGroups - does event have any event groups?
* @property {boolean} hasAvailableLanguages - does event have available languages?
* @property {string} hostStreamPageUrl - if isDigital, host stream url
* @property {string} viewStreamPageUrl - if isDigital, viewer stream url
* @property {string} shareUrl - url to share, if currently streaming and a free event, this will point to stream viewer page
* @property {boolean} isSoldOutOfGeneralTickets - if tickets were being sold, are we sold out of general tickets?
* @property {boolean} isSoldOutOfVipTickets - if isVipTicketed are we sold out of vip tickets?
* @property {boolean} isSoldOut - if ticketed are we sold out of all ticket classes?
* @property {boolean} hasTickets - if currentUser logged in, do they have tickets?
* @property {boolean} hasImageOrPromotionalVideo - does event have a cover image or promo video?
* @property {string} hostName - host full name
* @property {string} hostId - host id
* @property {string} hostAvatarUrl - host avatar url
* @property {string} hostTagLine - host tagLine
* @property {string} hostFirstName - host first name
* @property {string} hostLastName - host last name
* @property {boolean} isStreamEmbed - for streamType = 'resi', we use raw embed code
* @property {string} streamEmbed - for resi this is the raw embed code
* @property {string} inProgressAction - cta for splash page enum('watch', 'sold_out', 'buy_now', false)
* @property {boolean} hasInProgressAction - does it have an inProgress action?
* @property {boolean} hasInProgressActionWatch - is watch the inProgress cta?
* @property {boolean} hasInProgressActionBuyTickets - is buy_tickets the inProgress cta?
* @property {boolean} hasInProgressActionSoldOut - is sold_out the inProgress cta?
* @property {boolean} hasInProgressActionRegister - is register the inProgress cta?
* @property {boolean} hasInProgressActionUnregister - is unregister the inProgress cta?
* @property {boolean} secondsUntilStart - how many seconds until event begins? 0 if completed or started
* @property {boolean} hasStreamOrEmbed - does the record actually have a provider attached?
* @property {boolean} isDeleteable - this event deletable?
* @property {string} joinUrl - for Zoom, this is the viewer url
* @property {string} startUrl - for Zoom, this is the host url
* @property {number} providerId - provider id for event (must be passed with update call for a live stream)
* @property {boolean} isStreamLive - for digital events, did the host start the stream?
* @property {boolean} isStreamEnded - for digital events, did the host end the stream?
* @property {boolean} isStreamLiveAndUnpaused - for digital events, is the host currently streaming and the stream is unpaused?
* @property {boolean} isInterested - is user interested in event?
*/
prototype.normalizeEvent = function (_data) {
const data = this.normalizeData(_data)
if (!hasKey(data, 'vip_ticket_sales')) data.vip_ticket_sales = 0
if (!hasKey(data, 'general_ticket_sales')) data.general_ticket_sales = 0
if (!hasKey(data, 'general_ticket_count')) data.general_ticket_count = 0
if (!hasKey(data, 'vip_ticket_count')) data.vip_ticket_count = 0
if (!hasKey(data, 'is_interested')) data.is_interested = false
const mapProps = {
id: ['id', val => parseInt(val)],
count_down_display: ['withCountdownDisplay', val => !!val],
description_plain_text: 'description',
general_ticket_price: ['generalTicketPrice', val => val ? normalizeCurrency(val) : 0],
has_general_tickets_remaining: ['hasGeneralTicketsRemaining', (val) => !!val],
general_ticket_description_text: 'generalTicketDescription',
general_capacity: ['generalCapacity', (val) => val ? parseInt(val) : 0],
aggregate_interested_count: ['interestedCount', val => parseInt(val)],
vip_ticket_price: ['vipTicketPrice', (val) => val ? normalizeCurrency(val) : 0],
disclaimer_text: 'disclaimer',
email_content: 'emailContent',
has_vip_tickets_remaining: ['hasVipTicketsRemaining', (val) => !!val],
vip_ticket_description_text: 'vipTicketDescription',
vip_capacity: ['vipCapacity', val => parseInt(val)],
user_tickets_count: ['userTicketsCount', (val) => val ? parseInt(val) : 0],
name: 'title',
start_date: ['startDate', val => this.normalizeDate(val)],
end_date: ['endDate', val => this.normalizeDate(val)],
shop_url: 'shopUrl',
learnmore_url: ['learnMoreUrl', val => val ? this.normalizeUrl(val) : ''],
event_type: ['isPhysical', val => val.trim().toLowerCase() === 'physical'],
event_image_url: 'eventImageUrl',
available_languages: ['availableLanguages', (val) => val.filter(lan => !!lan)],
event_groups: ['eventGroupIds', (val) => val.map(id => parseInt(id))],
has_image: ['hasImage', (val) => !!val],
is_on_waitlist: ['isOnWaitlist', (val) => !!val],
event_room_member_id: 'eventRoomMemberId',
is_blocked: ['isBlocked', val => !!val],
is_host: ['isHost', val => !!val],
vip_ticket_sales: ['vipTicketSales', (val) => val ? normalizeCurrency(val) : 0],
general_ticket_sales: ['generalTicketSales', (val) => val ? normalizeCurrency(val) : 0],
general_ticket_count: ['generalTicketsSold', (val) => val ? parseInt(val) : 0],
vip_ticket_count: ['vipTicketsSold', (val) => val ? parseInt(val) : 0],
archive_date: ['archiveDate', (val) => this.normalizeDate(val)],
publish_date: ['publishDate', (val) => this.normalizeDate(val)],
visibility: ['isPrivate', (val) => val.trim().toLowerCase() === 'private'],
embed_code: 'promotionalVideoEmbedCode',
is_stream_ended: ['isStreamEnded', val => !!val],
is_stream_live: ['isStreamLive', val => !!val],
is_stream_live_and_unpaused: ['isStreamLiveAndUnpaused', val => !!val],
is_stream_paused: ['isStreamPaused', val => !!val],
is_stream_available: ['isStreamAvailable', val => !!val],
viewer_count: 'viewerCount',
is_interested: ['isInterested', val => !!val]
}
const ret = this.filterAndMapProps(data, mapProps)
ret.eventId = ret.id // legacy helper
ret.hasStreamOrEmbed = false
if (hasKey(data, 'provider') && data.provider) {
const tmp = this.normalizeEventProvider(data.provider)
ret.streamType = tmp.service
ret.streamUrl = tmp.embedUrl
ret.streamEmbed = tmp._orig
ret.hasStreamOrEmbed = true
ret.startUrl = data.provider?.start_url
ret.joinUrl = data.provider?.join_url
ret.providerId = data.provider?.id
ret.compareStreamType = textCompare(ret.streamType)
}
ret.isStreamEmbed = ret.streamType === 'resi'
ret.hasPromotionalVideo = false
if (hasKey(data, 'embed_code') && data.embed_code) {
const tmp = this.normalizeEmbedCode(data.embed_code)
ret.promotionalVideoType = tmp.service
ret.promotionalVideoUrl = tmp.embedUrl
ret.hasPromotionalVideo = true
}
ret.isPublic = !ret.isPrivate
ret.isDigital = !ret.isPhysical
ret.totalTicketSales = ret.generalTicketSales + ret.vipTicketSales
ret.formattedTotalTicketSales = formatCurrency(ret.totalTicketSales)
ret.totalTicketsSold = ret.generalTicketsSold + ret.vipTicketsSold
ret.isGeneralTicketed = Boolean(ret.generalTicketPrice > 0)
ret.isVipTicketed = Boolean(ret.vipTicketPrice > 0)
ret.isTicketed = ret.isGeneralTicketed
ret.isFree = !ret.isTicketed
ret.hasTicketsRemaining = Boolean(ret.hasVipTicketsRemaining || ret.hasGeneralTicketsRemaining)
ret.isPublished = Boolean(!ret.publishDate || isPastDate(ret.publishDate))
ret.isArchived = true
if (!ret.archiveDate || isFutureDate(ret.archiveDate)) ret.isArchived = false
ret.isCurrentlySellingGeneralTickets = Boolean(ret.isGeneralTicketed && !ret.isArchived && ret.isPublished)
ret.isCurrentlySellingVipTickets = Boolean(ret.isVipTicketed && !ret.isArchived && ret.isPublished)
ret.isCurrentlySellingTickets = Boolean(ret.isCurrentlySellingGeneralTickets || ret.isCurrentlySellingVipTickets)
ret.hasEnded = isPastDate(ret.endDate)
ret.isPastDate = ret.hasEnded // legacy alias
ret.hasStarted = isPastDate(ret.startDate)
ret.isInProgress = Boolean(ret.hasStarted && !ret.hasEnded)
ret.isKwivrrStream = Boolean(ret.isDigital && ret.compareStreamType === 'kwivrr')
ret.eventPageUrl = this.siteUrl + '/#view=event&eventId=' + ret.id
ret.isCurrentlyStreaming = Boolean(ret.isInProgress && ret.isDigital)
ret.isLive = ret.isInProgress // legacy alias
const kwivvrHostUrl = this.siteUrl + '/#view=livestream-pub&eventId=' + ret.id
const kwivvrViewerUrl = this.siteUrl + '/#view=livestream-sub&eventId=' + ret.id
const livestreamEmbedUrl = this.siteUrl + '/#view=livestream-embed&eventId=' + ret.id
ret.hostStreamUrl = null
ret.viewStreamUrl = null
if (ret.isDigital) {
ret.hostStreamUrl = ret.isKwivrrStream ? kwivvrHostUrl : livestreamEmbedUrl
ret.viewStreamUrl = ret.isKwivrrStream ? kwivvrViewerUrl : livestreamEmbedUrl
}
ret.shareUrl = this.getShareUrl(`/api/v1/events/${ret.id}/share`)
// if (ret.isCurrentlyStreaming && !ret.isTicketed) ret.shareUrl = ret.streamViewerUrl
ret.hasEventGroups = Boolean(ret.eventGroupIds.length > 0)
ret.hasAvailableLanguages = Boolean(ret.availableLanguages.length > 0)
ret.descriptionText = stripHtml(ret.description)
ret.emailContentText = stripHtml(ret.emailContent)
ret.disclaimerText = stripHtml(ret.disclaimer)
ret.generalTicketDescriptionText = stripHtml(ret.generalTicketDescription)
ret.vipTicketDescriptionText = stripHtml(ret.vipTicketDescription)
ret.isSoldOutOfGeneralTickets = Boolean(ret.isGeneralTicketed && !ret.hasGeneralTicketsRemaining)
ret.isSoldOutOfVipTickets = Boolean(ret.isVipTicketed && !ret.hasVipTicketsRemaining)
ret.isSoldOut = Boolean(ret.isTicketed && ret.isSoldOutOfGeneralTickets && (ret.isSoldOutOfVipTickets || !ret.isVipTicketed))
ret.hasTickets = Boolean(ret.userTicketsCount > 0)
ret.hasImageOrPromotionalVideo = Boolean(ret.hasImage || ret.hasPromotionalVideo)
ret.hostName = `${data.host.firstname} ${data.host.lastname}`
ret.hostId = parseInt(data.host.id)
ret.hostAvatarUrl = data.host.profile.avatar_url
ret.hostTagLine = data.host.profile.tagline
ret.hostFirstName = data.host.firstname
ret.hostLastName = data.host.lastname
ret.inProgressAction = this.normalizeInProgressAction(ret)
ret.hasInProgressAction = ret.inProgressAction !== false
ret.hasInProgressActionStartStreaming = ret.inProgressAction === 'start_streaming'
ret.hasInProgressActionWatch = ret.inProgressAction === 'watch'
ret.hasInProgressActionBuyTickets = ret.inProgressAction === 'buy_tickets'
ret.hasInProgressActionSoldOut = ret.inProgressAction === 'sold_out'
ret.secondsUntilStart = 0
if (!ret.isInProgress && !ret.hasEnded) ret.secondsUntilStart = calculateSecondsBetween(ret.startDate, new Date())
ret.formattedGeneralTicketPrice = formatCurrency(ret.generalTicketPrice)
ret.formattedVipTicketPrice = formatCurrency(ret.vipTicketPrice)
ret.isDeleteable = hasKey(data, 'is_deleteable') ? !!data.is_deleteable : true
const getIsCheckInTime = () => {
var now = new Date()
var oneHourFromNow = new Date(now)
oneHourFromNow.setHours(oneHourFromNow.getHours() + 1)
const startDate = new Date(ret.startDate)
var c = new Date(oneHourFromNow.getTime())
c.setDate(c.getDate() + 1)
const isWithinAnHourOfStarting = startDate >= now && startDate < c
return isWithinAnHourOfStarting || ret.isLive
}
const getNumWatching = (num) => {
if (num >= 1000000000) {
return (num / 1000000000).toFixed(1).replace(/\.0$/, '') + 'B'
}
if (num >= 1000000) {
return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M'
}
if (num >= 1000) {
return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'K'
}
return num
}
ret.numWatching = getNumWatching(2100)
ret.isCheckInTime = getIsCheckInTime()
return ret
}
prototype.normalizeEventOrder = function (_data) {
const data = this.normalizeData(_data)
return data
}
prototype.normalizeEventManagementPayload = function (payload, mode = 'create') {
const _normalizeStreamType = (st) => {
const testSt = textCompare(st)
switch (testSt) {
case 'jitsi': return 'Jitsi'
case 'zoom': return 'Zoom'
}
return st
}
let {
providerId,
eventSpeakers,
isTicketed,
title,
isPhysical,
streamType,
embedCode,
isPrivate,
eventImageUrl,
promotionalVideoEmbedCode,
description,
disclaimer,
emailContent,
shopUrl,
learnMoreUrl,
availableLanguages,
eventGroupIds,
isStartingNow,
startDate,
endDate,
publishDate,
archiveDate,
withCountdownDisplay,
generalTicketPrice,
generalCapacity,
generalTicketDescription,
vipTicketPrice,
vipCapacity,
vipTicketDescription
// isAllowingLocalDeals
} = payload
const testStreamType = streamType && textCompare(streamType)
const getDate = date => date === null ? date : new Date(date)
const eventPayload = {
event_type: isPhysical ? 1 : 0,
visibility: isPrivate ? 1 : 0,
name: title,
description: description,
disclaimer: disclaimer,
email_content: emailContent,
event_image_url: eventImageUrl,
// "???": "", // Splash page url?
learnmore_url: learnMoreUrl,
available_languages: availableLanguages,
// "event_group_events_attributes": [] // Array of ids: [1,2,3]
// "???": ?, // Custom timezone?
start_date: getDate(isStartingNow ? Date.now() : startDate),
end_date: getDate(endDate),
publish_date: getDate(publishDate),
archive_date: getDate(archiveDate),
count_down_display: withCountdownDisplay ? 1 : 0,
event_speakers: eventSpeakers
// addons_attributes: [
// {
// name: 'low-end souvenir',
// description: 'a cheap trinket',
// price: 10,
// capacity: 100,
// product_type: 'addon'
// },
// {
// name: 'high-end souvenir',
// description: 'a _gold_ cheap trinket',
// price: 50,
// capacity: 101,
// product_type: 'addon'
// }
// ]
}
if (eventImageUrl && eventImageUrl.length > 0) {
if (isBase64(eventImageUrl)) {
eventPayload.event_image_base64 = cleanBase64(eventImageUrl)
} else {
eventPayload.event_image_url = eventImageUrl
}
}
if (!isPhysical) {
eventPayload.shop_url = shopUrl
switch (testStreamType) {
case 'kwivrr':
case 'jitsi':
embedCode = '<iframe></iframe>'
break
case 'resi':
// embedCode = embedCode
break
case 'zoom':
break
default:
embedCode = createEmbedCode(embedCode, streamType)
break
}
eventPayload.provider_attributes = {
provider_type: _normalizeStreamType(streamType),
embed_code: embedCode
}
// eventPayload.stream_type = streamType === 'zoom' ? 'Zoom' : streamType
// eventPayload.stream_type = streamType === 'jitsi' ? 'Jitsi' : streamType
}
if (isTicketed) {
const createTickets = (name, capacity, price, description, arr) => {
if (!capacity || !price) return
capacity = parseFloat(capacity)
price = parseFloat(price)
if (!isNaN(vipCapacity) && !isNaN(vipTicketPrice)) {
arr.push({
name,
description,
price,
capacity
})
}
}
const tickets = []
createTickets('general', generalCapacity, generalTicketPrice, generalTicketDescription, tickets)
createTickets('vip', vipCapacity, vipTicketPrice, vipTicketDescription, tickets)
eventPayload.ticketing_types_attributes = tickets
}
if (promotionalVideoEmbedCode) {
const tmp = parseEmbedCode(promotionalVideoEmbedCode)
eventPayload.embed_code = tmp.embedCode
}
if (hasKey(payload, 'eventGroupIds')) {
eventPayload.event_group_events_attributes = eventGroupIds.map(event_group_id => {
return { event_group_id }
})
}
if (!startDate && !endDate) {
eventPayload.start_date = new Date()
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
eventPayload.end_date = tomorrow
}
if (mode === 'update') {
eventPayload.provider_id = providerId
}
console.log('##eventPayload', eventPayload)
return eventPayload
}
prototype._associateEventSpeaker = function ({ eventId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${eventId}/speakers`),
method: 'POST',
requestType: 'json',
data: {
...rest
}
}
return this.authenticatedFetchWrap(payload)
}
/**
* @typedef {object} EventSpeaker
* @property {number} eventSpeakerJoinId - join entity id (used for delete/update actions)
* @property {number} id - entity id (kwivvrId or speakerProfileId)
* @property {string} name - speaker name
* @property {string} topic - topic to discuss
* @property {string} description - more details on topic
* @property {string} startTime - when speaker goes on
* @property {string} endTime - when speaker goes off
* @property {string} avatarUrl - speaker's avatar
* @property {boolean} isKwivvrUser - is "id" a kwivrr account id or a speaker profile id?
*/
/**
* @function associateEventSpeaker
* @param {object} payload - The eventSpeaker payload
* @param {number} payload.eventId - event id
* @param {number} [payload.kwivrrId] - associate by user id if exists, otherwise, check for speaker profile id
* @param {number} [payload.speakerProfileId] - associate by speaker_profile_id (fallthrough if kwivrrId not provided)
* @param {string} payload.topic - topic speaker is covering
* @param {string} payload.description - more detailed description of topic
* @param {string} payload.startTime - when speaker goes on
* @param {string} payload.endTime - when speaker's session is over
* @returns {EventSpeaker} - EventSpeaker instance
* @example
*
* associateEventSpeaker({
* eventId: 100,
* speakerProfileId: 6,
* topic:"9d3fjpsarasdgywm7lj8snq5pr39e3",
* description:"5zbn5idwlpvbg3miqidwwm2pvkxohm",
* startTime:"2021-07-07T07:39:05.797-05:00",
* endTime:"2021-07-09T07:39:05.797-05:00"
* })
*/
prototype.associateEventSpeaker = function (payload) {
const {
eventId,
topic,
description,
startTime,
endTime
} = payload
const finalPayload = {
eventId,
key_note: topic,
description: description,
start_time: startTime,
end_time: endTime
}
if (hasKey(payload, 'kwivrrId')) finalPayload.kwivrr_id = payload.kwivrrId
else if (hasKey(payload, 'speakerProfileId')) finalPayload.speaker_profile_id = payload.speakerProfileId
const raw = ifPromise(payload, () => this._associateEventSpeaker(finalPayload))
return raw
.then(res => this.normalizeEventSpeaker(res.data))
.catch(error => this.onError('associateEventSpeaker', error))
}
prototype._createEvent = function ({ userId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/events`),
method: 'POST',
requestType: 'json',
data: rest
}
const self = this
const ret = this.authenticatedFetchWrap(payload)
.then(res => {
const event = self.normalizeEvent(res.data)
if (!hasKey(rest.zevents_event, 'event_speakers')) return event
rest.zevents_event.event_speakers.forEach(({ speaker, topic, description, startTime, endTime }) => {
const finalPayload = {
eventId: event.id,
topic: topic,
description: description,
startTime: startTime,
endTime: endTime
}
if (speaker.isKwivrrUser) finalPayload.kwivrrId = speaker.id
else finalPayload.speakerProfileId = speaker.id
self.associateEventSpeaker(finalPayload)
})
return event
})
return ret
}
/**
* createEvent function
*
* @param {object} payload - The event payload
* @param {string} [payload.userId='me'] - userId to event
* @param {string} payload.title - title of event
* @param {string} payload.startDate - startDate of event
* @param {string} payload.endDate - endDate of event
* @param {string} payload.publishDate - publishDate of event
* @param {string} payload.promotionalVideoEmbedCode - publishDate of event
* @param {string} payload.generalCapacity - generalCapacity of event
* @param {string} payload.vipCapacity - vipCapacity of event
* @param {string} payload.generalTicketPrice - generalTicketPrice of event
* @param {string} payload.vipTicketPrice - vipTicketPrice of event
* @param {string} payload.archiveDate - archiveDate of event
* @param {string} payload.eventType - eventType of event
* @param {boolean} payload.visibility - visibility of event (isPrivate)
* @param {string} payload.streamType - streamType of event (vimeo, youtube, etc)
* @param {string} payload.shopUrl - shopUrl of event
* @param {string} payload.learnmoreUrl - learnmoreUrl of event
* @param {Array} payload.availableLanguages - availableLanguages of event
* @param {boolean} payload.countDownDisplay - event has countdown or not
* @param {Array} payload.eventGroupEventsAttributes - eventGroupEventsAttributes of event
* @example
*
* createEvent({
* userId,
* title,
* startDate,
* endDate,
* publishDate,
* promotionalVideoEmbedCode,
* generalCapacity,
* vipCapacity,
* generalTicketPrice,
* vipTicketPrice,
* archiveDate,
* eventType,
* isPrivate,
* streamType,
* shopUrl,
* learnmoreUrl,
* availableLanguages,
* countDownDisplay,
* eventGroupEventsAttributes
* })
* @returns {object}
* {
id,
type,
name,
location,
startDate,
endDate,
promotionalVideoEmbedCode,
state,
userId,
tenantId,
liveStreamName,
broadcasted,
streamType,
shopUrl,
learnmoreUrl,
eventType,
shareUrl,
eventImageUrl,
host,
eventGroupEventsAttributes
}
*/
prototype.createEvent = function (payload) {
const { userId, ...rest } = payload
console.log('##payload ', rest)
const normalizedEventPayload = this.normalizeEventManagementPayload(rest)
const finalPayload = {
userId: this.normalizeUserId(userId),
zevents_event: normalizedEventPayload
}
const raw = ifPromise(payload, () => this._createEvent(finalPayload))
return raw
.then(res => res)
.catch(error => this.onError('createEvent', error))
}
prototype._updateEvent = function ({ userId, id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/events/${id}`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
// name: title,
// start_date,
// end_date,
// publish_date,
// embed_code,
// general_capacity,
// vip_capacity,
// general_ticket_price,
// vip_ticket_price,
// archive_date,
// event_type: type,
// user_id,
// visibility: isPrivate,
// livestream_type: provider,
// shop_url,
// learnmore_url,
// available_languages,
// count_down_display,
// event_group_events_attributes,
// need: coverImageUrl, description, disclaimer, hasCustomTimeZone, generalTicketDescriptionText, vipTicketDescriptionText
/**
* @function updateEvent
* @param {object} payload - The event payload
* @param {string} [payload.userId='me'] - userId to event
* @param {string} payload.title - title of event
* @param {string} payload.startDate - startDate of event
* @param {string} payload.endDate - endDate of event
* @param {string} payload.publishDate - publishDate of event
* @param {string} payload.embedCode - publishDate of event
* @param {string} payload.generalCapacity - generalCapacity of event
* @param {string} payload.vipCapacity - vipCapacity of event
* @param {string} payload.generalTicketPrice - generalTicketPrice of event
* @param {string} payload.vipTicketPrice - vipTicketPrice of event
* @param {string} payload.archiveDate - archiveDate of event
* @param {string} payload.eventType - eventType of event
* @param {boolean} payload.visibility - visibility of event (isPrivate)
* @param {string} payload.streamType - streamType of event (vimeo, youtube, etc)
* @param {string} payload.shopUrl - shopUrl of event
* @param {string} payload.learnmoreUrl - learnmoreUrl of event
* @param {Array} payload.availableLanguages - availableLanguages of event
* @param {boolean} payload.countDownDisplay - event has countdown or not
* @param {Array} payload.eventGroupEventsAttributes - eventGroupEventsAttributes of event
* @example
*
* updatEvent({
* title,
* startDate,
* endDate,
* publishDate,
* embedCode,
* generalCapacity,
* vipCapacity,
* generalTicketPrice,
* vipTicketPrice,
* archiveDate,
* eventType,
* isPrivate,
* streamType,
* shopUrl,
* learnmoreUrl,
* availableLanguages,
* countDownDisplay,
* eventGroupEventsAttributes
* })
* @returns {object}
* {
id,
type,
name,
location,
startDate,getEve
state,
userId,
tenantId,
liveStreamName,
broadcasted,
streamType,
shopUrl,
learnmoreUrl,
eventType,
shareUrl,
eventImageUrl,
host,
eventGroupEventsAttributes
}
*/
prototype.updateEvent = function (payload) {
const { userId, id, ...rest } = payload
console.log('##payload ', rest)
const normalizedEventPayload = this.normalizeEventManagementPayload(rest, 'update')
const finalPayload = {
userId: this.normalizeUserId(userId),
id: id,
zevents_event: normalizedEventPayload
}
const raw = ifPromise(payload, () => this._updateEvent(finalPayload))
return raw
.then(res => this.normalizeEvent(res.data))
.catch(error => this.onError('updateEvent', error))
}
prototype._deleteEvent = function ({ userId, id }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/events/${id}`),
method: 'DELETE'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function deleteEvent
* @param {object} payload - The event payload
* @param {string} [payload.userId='me'] - userId tied to event
* @param {string} payload.id - id of event
* @returns {void}
* @example
*
* deleteEvent({
* id: 123
* })
*/
prototype.deleteEvent = function (payload) {
const {
userId,
id
} = payload
const finalPayload = {
userId: this.normalizeUserId(userId),
id
}
const raw = ifPromise(payload, () => this._deleteEvent(finalPayload))
return raw
.catch(error => this.onError('deleteEvent', error))
}
prototype._disjoinEventSpeaker = function ({ eventId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${eventId}/speakers`),
method: 'DELETE',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function disjoinEventSpeaker
* @param {object} payload - The event payload
* @param {number} payload.eventId - eventId speaker is assigned to
* @param {string} payload.eventSpeakerJoinId - event speaker join table id
* @returns {void}
* @example
*
* deleteEvent({
* eventId: 100,
* eventSpeakerJoinId: 3838
* })
*/
prototype.disjoinEventSpeaker = function (payload) {
const finalPayload = {
eventId: payload.eventId,
speaker_event_join_id: payload.eventSpeakerJoinId
}
const raw = ifPromise(payload, () => this._disjoinEventSpeaker(finalPayload))
return raw
.catch(error => this.onError('disjoinEventSpeaker', error))
}
prototype._getEvent = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}`),
method: 'GET',
data: rest
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @function getEvent
* @param {object} payload - The event payload
* @param {number} payload.id - eventId
* @returns {Event} - Event object
* @example
*
* getEvent({
* id: 100
* })
*/
prototype.getEvent = function (payload) {
const finalPayload = {
id: payload.id
}
if (payload.withChat) finalPayload.with_chat = true
const raw = ifPromise(payload, () => this._getEvent(finalPayload))
return raw
.then(res => this.normalizeEvent(res.data))
.catch(error => this.onError('getEvent', error))
}
prototype.getEventWithChat = function (payload) {
const finalPayload = {
...payload,
withChat: true
}
return this.getEvent(finalPayload)
}
prototype._getEventReceipt = function ({ eventId }) {
const payload = {
url: this.getUrl(`/api/v1/events/${eventId}/receipt`),
method: 'GET'
}
return this.authenticatedFetchWrap(payload)
}
prototype.getEventReceipt = function (payload) {
const finalPayload = {
eventId: payload.eventId
}
const raw = ifPromise(payload, () => this._getEventReceipt(finalPayload))
return raw
.then(res => {
this.normalizeListData(res.data, this.normalizeEventOrder)
})
.catch(error => this.onError('getEventReceipt', error))
}
prototype._getEvents = function (data) {
const payload = {
url: this.getUrl('/api/v1/events'),
method: 'GET',
data
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @typedef {object} getEventsListSummary
* @property {number} page - page of results
* @property {number} perPage - results per page
* @property {boolean} hasMore - are more pages available?
*/
/**
* @typedef {object} getEventsCriteriaSummary
* @property {string} scope - scope
* @property {string} startDate - startDate
* @property {string} endDate - endDate
* @property {number} hostId - hostId
* @property {number} userId - userId
*/
/**
* @typedef {object} getEventsReturn
* @property {Event[]} entries - list of users returned
* @property {getEventsListSummary} listSummary - list summary object
* @property {getEventsCriteriaSummary} criteriaSummary - list summary object (empty)
*/
/**
* @function getEvents
* @param {object} payload - The payload
* @param {number} payload.scope - scope to limit returns to (completed, upcoming, attended, registeredFor)
* @param {number} [payload.page=1] - page of results
* @param {string} [payload.selectedDate] - for ranged queries, start of range
* @returns {getEventsReturn}
* @example
*
* getEvents({
* scope: 'completed'
* })
*/
prototype.getEvents = function (payload) {
const {
scope,
...rest
} = payload
const finalPayload = {
scope: scope,
...rest
}
const raw = ifPromise(payload, () => this._getEvents(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeEvent))
.catch(error => this.onError('getEvents', error))
}
prototype.getEventsHosting = function (payload) {
const finalPayload = {
scope: 'hosting',
page: payload?.page || 1
}
if (hasKey(payload, 'selectedDate')) finalPayload.date = payload.selectedDate
return this.getEvents(finalPayload)
}
prototype.getEventsHosted = function (payload) {
const finalPayload = {
scope: 'completed',
page: payload?.page || 1
}
if (hasKey(payload, 'selectedDate')) finalPayload.date = payload.selectedDate
return this.getEvents(finalPayload)
}
prototype.getEventsAttending = function (payload) {
const finalPayload = {
scope: 'registeredFor',
page: payload?.page || 1
}
if (hasKey(payload, 'selectedDate')) finalPayload.date = payload.selectedDate
return this.getEvents(finalPayload)
}
prototype.getEventsAttended = function (payload) {
const finalPayload = {
scope: 'attended',
page: payload?.page || 1
}
if (hasKey(payload, 'selectedDate')) finalPayload.date = payload.selectedDate
return this.getEvents(finalPayload)
}
prototype._getEventsByUserId = function ({ userId, scope, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/feed?scope=${scope}`),
method: 'GET',
data: rest
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @typedef {object} getEventsByUserIdListSummary
* @property {number} page - page of results
* @property {number} perPage - results per page
* @property {boolean} hasMore - are more pages available?
*/
/**
* @typedef {object} getEventsByUserIdCriteriaSummary
* @property {string} scope - scope
* @property {number} userId - userId
*/
/**
* @typedef {object} getEventsByUserIdReturn
* @property {Event[]} entries - list of users returned
* @property {getEventsByUserIdListSummary} listSummary - list summary object
* @property {getEventsByUserIdCriteriaSummary} criteriaSummary - list summary object (empty)
*/
/**
* @function getEventsByUserId
* @param {object} payload - The payload
* @param {number} payload.scope - scope to limit returns to (attended, attending, hosted, hosting)
* @param {number} [payload.page=1] - page of results
* @param {number} [payload.userId] - user's id
* @returns {getEventsByUserIdReturn}
* @example
*
* getEventsByUserId({
* scope: 'completed'
* })
*/
prototype.getEventsByUserId = function (payload) {
const {
scope,
userId,
page
} = payload
const finalPayload = {
scope,
userId,
page
}
const raw = ifPromise(payload, () => this._getEventsByUserId(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeEvent))
.catch(error => this.onError('getEventsByUserId', error))
}
prototype.getEventsHostingByUserId = function ({ userId, page }) {
const finalPayload = {
scope: 'hosting',
userId,
page
}
return this.getEventsByUserId(finalPayload)
}
prototype.getEventsHostedByUserId = function ({ userId, page }) {
const finalPayload = {
scope: 'hosted',
userId,
page
}
return this.getEventsByUserId(finalPayload)
}
prototype.getEventsAttendingByUserId = function ({ userId, page }) {
const finalPayload = {
scope: 'attending',
userId,
page
}
return this.getEventsByUserId(finalPayload)
}
prototype.getEventsAttendedByUserId = function ({ userId, page }) {
const finalPayload = {
scope: 'attended',
userId,
page
}
return this.getEventsByUserId(finalPayload)
}
prototype._getEventServiceFees = function ({ accessToken, ...rest }) {
const payload = {
url: this.getUrl('/api/v1/events/service_fees'),
method: 'GET',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @typedef {object} EventServiceFeeTableRow
* @property {number} gte - greater-than-or-equal
* @property {number} lt - less than, if null, this is the max bucket
* @property {number} fee - per ticket fee for this row
*/
/**
* @function getEventServiceFees
* @returns {EventServiceFeeTableRow[]} - row of service fee table
* @example
*
* getEventServiceFees()
*/
prototype.getEventServiceFees = function (payload) {
const raw = ifPromise(payload, () => this._getEventServiceFees())
return raw
.then(res => res.data)
.catch(error => this.onError('getEventServiceFees', error))
}
prototype._getEventHostTickets = function ({ event_id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${event_id}/host_tickets`),
method: 'GET',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
prototype.isInState = function (currentState, stateToTest) {
const finalTest = normalizeArray(stateToTest)
return finalTest.includes(currentState)
}
prototype.isActiveTicket = function (ticket) {
return this.isActiveTicketState(ticket.ticketState)
}
prototype.isActiveTicketState = function (state) {
return !this.isInState(state, [
'REVOKED',
'REFUNDED',
'PARTIALLY_REFUNDED'
])
}
prototype.isCheckInableTicket = function (ticket) {
if (ticket.isCheckedIn) return false
return ticket.isActive
}
prototype.isUncheckInableTicket = function (ticket) {
if (!ticket.isCheckedIn) return false
return ticket.isActive
}
prototype.isUpgradeableTicket = function (ticket) {
if (ticket.isVip) return false
if (!ticket.hasVip) return false
if (ticket.isTransferred) return false
return ticket.isActive
}
prototype.isTransferrableTicket = function (ticket) {
if (ticket.isCheckedIn) return false
if (this.isInState(ticket.ticketState, 'PENDING_TRANSFER')) return false
if (this.isInState(ticket.ticketState, 'FORCE_TRANSFERRED')) return false
// ok it's not checked in and it's not in a pending transfer nor force transferred state (you'd need to reclaim first)
// let's verify it's still active
return ticket.isActive
}
prototype.isPendingTransferTicket = function (ticket) {
if (!ticket.isActive) return false
return this.isInState(ticket.ticketState, 'PENDING_TRANSFER')
}
prototype.isForceTransferrableTicket = function (ticket) {
return this.isTransferrableTicket(ticket)
}
prototype.isReclaimableTicket = function (ticket) {
if (ticket.isGiftedToMe) return false
if (ticket.isCheckedIn) return false
if (!ticket.isActive) return false
if (this.isInState(ticket.ticketState, 'PENDING_TRANSFER')) return true
return ticket.isTransferred
}
prototype.isRevokableTicket = function (ticket) {
if (ticket.isCheckedIn) return false
return ticket.isActive
}
prototype.isSetCredentialsableTicket = function (ticket) {
// this can always be done on an active ticket
return ticket.isActive
}
prototype.isTransferredTicket = function (ticket) {
if (!ticket.giftedUserEmail) return false
return this.isInState(ticket.ticketState, ['CLAIMED', 'FORCE_TRANSFERRED'])
}
prototype.isPartiallyRefundedTicket = function (ticket) {
return this.isInState(ticket.ticketState, 'PARTIALLY_REFUNDED')
}
prototype.isRefundedTicket = function (ticket) {
return this.isInState(ticket.ticketState, 'REFUNDED')
}
prototype.isRevokedTicket = function (ticket) {
return this.isInState(ticket.ticketState, 'REVOKED')
}
prototype.normalizeEventHostTicketEntry = function (_data) {
const data = this.normalizeData(_data)
const ticketState = this.mapApiStatus(data.api_status)
const ret = {
qrCode: data.qr_code,
currentVipPrice: normalizeCurrency(data.event.vip_ticket_price),
currentGeneralPrice: normalizeCurrency(data.event.general_ticket_price),
id: parseInt(data.id),
isCheckedIn: !!data.is_checked_in,
orderId: parseInt(data.order_id),
currentTicketHolder: data.ticket_holder.email,
purchasedBy: data.buyer.email,
purchasedDatetime: this.normalizeDate(data.purchased_datetime),
ticketType: data.type,
price: normalizeCurrency(data.price),
credentialsId: data.credentials_id ? parseInt(data.credentials_id) : undefined,
ticketState,
giftedUserEmail: data?.gifted_user_email
}
ret.isVip = ret.ticketType === 'vip'
ret.isGeneral = !ret.isVip
ret.hasVip = ret.currentVipPrice > 0
ret.isActive = this.isActiveTicket(ret)
ret.isTransferred = this.isTransferredTicket(ret)
ret.isCheckInable = this.isCheckInableTicket(ret)
ret.isUncheckInable = this.isUncheckInableTicket(ret)
ret.isTransferrable = this.isTransferrableTicket(ret)
ret.isReclaimable = this.isReclaimableTicket(ret)
ret.isSetCredentialsable = this.isSetCredentialsableTicket(ret)
ret.isRevokable = this.isRevokableTicket(ret)
ret.isPendingTransfer = this.isPendingTransferTicket(ret)
ret.isPartiallyRefunded = this.isPartiallyRefundedTicket(ret)
ret.isRefunded = this.isRefundedTicket(ret)
ret.isRevoked = this.isRevokedTicket(ret)
ret.isUpgradeable = this.isUpgradeableTicket(ret)
ret.formattedPrice = formatCurrency(ret.price)
return ret
}
prototype.getEventHostTickets = function (payload) {
const finalPayload = {
event_id: payload.eventId,
page: payload.page || 1
}
if (payload.term) finalPayload.term = payload.term
const raw = ifPromise(payload, () => this._getEventHostTickets(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeEventHostTicketEntry))
.catch(error => this.onError('getEventHostTickets', error))
}
prototype._searchEvents = function (data) {
const payload = {
url: this.getUrl('/api/v1/events/search'),
method: 'GET',
requestType: 'json',
data
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @typedef {object} searchEventsListSummary
* @property {number} page - page of results
* @property {number} perPage - results per page
* @property {boolean} hasMore - are more pages available?
*/
/**
* get url from video embed code
*
* @typedef {object} searchEventsReturn
* @property {Event[]} entries - list of speaker profile ids returned
* @property {searchEventsListSummary} listSummary - list summary object
* @property {object} criteriaSummary - criteria summary object (empty)
*/
/**
* @function getEventSpeakers
* @param {object} payload - The payload
* @param {number} payload.id - event id
* @param {number} [payload.page=1] - result set page
* @returns {getEventSpeakersReturn} - getEventSpeakersReturn
* @example
* getEventSpeakers({
* page: 2
* })
*/
/**
* @function searchEvents
* @param {object} payload - The payload
* @param {number} [payload.page=1] - result set page
* @param {string} payload.term - term to search against
* @returns {searchEventsReturn} - searchEvents instance
* @example
*
* searchEvents({
* term: 'foo',
* page: 2
* })
*/
prototype.searchEvents = function (payload) {
const finalPayload = {
term: payload.term,
page: payload?.page || 1
}
const raw = ifPromise(payload, () => this._searchEvents(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeEvent))
.catch(error => this.onError('searchEvents', error))
}
prototype._showEventSpeaker = function ({ id, eventSpeakerJoinId }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/speakers/${eventSpeakerJoinId}`),
method: 'GET'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function showEventSpeaker
* @param {object} payload - The eventSpeaker payload
* @param {number} payload.id - event id
* @param {number} payload.speakerEventJoinId - event speaker join id
* @returns {EventSpeaker} - EventSpeaker instance
* @example
*
* showEventSpeaker({
* id: 10,
* speakerEventJoinId: 100
* })
*/
prototype.showEventSpeaker = function (payload) {
const finalPayload = {
id: payload.id,
eventSpeakerJoin: payload.eventSpeakerJoin
}
const raw = ifPromise(payload, () => this._showEventSpeaker(finalPayload))
return raw
.then(res => this.normalizeEventSpeaker(res.data))
.catch(error => this.onError('showEventSpeaker', error))
}
prototype._getEventSpeakers = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/speakers`),
method: 'GET',
requestType: 'json',
data: rest
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} getEventSpeakerSummary
* @property {number} page - page of results
* @property {number} perPage - results per page
* @property {boolean} hasMore - are more pages available?
*/
/**
* @typedef {object} getEventSpeakersReturn
* @property {EventSpeaker[]} entries - list of speaker profile ids returned
* @property {getEventSpeakerSummary} listSummary - list summary object
* @property {object} criteriaSummary - criteria summary object (empty)
*/
/**
* @function getEventSpeakers
* @param {object} payload - The payload
* @param {number} payload.id - event id
* @param {number} [payload.page=1] - result set page
* @returns {getEventSpeakersReturn} - getEventSpeakersReturn
* @example
*
* getEventSpeakers({
* id: 10,
* page: 2
* })
*/
prototype.getEventSpeakers = function (payload) {
const finalPayload = {
id: payload.id,
page: payload?.page || 1
}
const raw = ifPromise(payload, () => this._getEventSpeakers(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeEventSpeaker))
.catch(error => this.onError('getEventSpeakers', error))
}
prototype._updateEventSpeaker = function ({ eventId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${eventId}/speakers`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function updateEventSpeaker
* @param {object} payload - The eventSpeaker payload
* @param {number} payload.eventId - event id
* @param {number} payload.speakerEventJoinId - event speaker join id
* @param {number} [payload.kwivrrId] - associate by user id if exists, otherwise, check for speaker profile id
* @param {number} [payload.speakerProfileId] - associate by speaker_profile_id (fallthrough if kwivrrId not provided)
* @param {string} payload.topic - topic speaker is covering
* @param {string} payload.description - more detailed description of topic
* @param {string} payload.startTime - when speaker goes on
* @param {string} payload.endTime - when speaker's session is over
* @returns {EventSpeaker} - EventSpeaker instance
* @example
*
* updateEventSpeaker({
* eventId: 10,
* speakerEventJoinId: 100,
* speakerProfileId: 6,
* topic:"9d3fjpsarasdgywm7lj8snq5pr39e3",
* description:"5zbn5idwlpvbg3miqidwwm2pvkxohm",
* startTime:"2021-07-07T07:39:05.797-05:00",
* endTime:"2021-07-09T07:39:05.797-05:00"
* })
*/
prototype.updateEventSpeaker = function (payload) {
const finalPayload = {
eventId: payload.eventId,
speaker_event_join_id: payload.eventSpeakerJoinId,
key_note: payload.topic,
description: payload.description,
start_time: payload.startTime,
end_time: payload.endTime
}
if (hasKey(payload, 'kwivrrId')) finalPayload.kwivrr_id = payload.kwivrrId
else if (hasKey(payload, 'speakerProfileId')) finalPayload.speaker_profile_id = payload.speakerProfileId
const raw = ifPromise(payload, () => this._updateEventSpeaker(finalPayload))
return raw
.then(res => this.normalizeEventSpeaker(res.data))
.catch(error => this.onError('updateEventSpeaker', error))
}
prototype._getEventCalendar = function (data) {
const payload = {
url: this.getUrl('/api/v1/events/calendar'),
method: 'GET',
requestType: 'json',
data
}
return this.authenticatedFetchWrap(payload)
}
prototype.normalizeEventCalendar = function (data) {
const dates = {}
const keysWeCareAbout = ['completed', 'attended', 'registered_for', 'hosting']
keysWeCareAbout.forEach(k => {
if (hasKey(data, k)) {
const obj = data[k]
for (const [dateString, count] of Object.entries(obj)) {
if (!hasKey(dates, dateString)) dates[dateString] = 0
dates[dateString] = dates[dateString] + parseInt(count)
}
}
})
return dates
}
/**
* @function getEventCalendar
* @param {object} payload - The event payload
* @param {string} [payload.userId] - userId to masquerade if needed
* @param {string} payload.startDate - start date of lookup range
* @param {string} payload.endDate - end date of lookup range
* @returns {object} - object with date strings as keys and counts for that date
* @example
* getEventCalendar({
* startDate: '2021-02-01'
* endDate: '2021-03-01'
* })
*/
prototype.getEventCalendar = function (payload) {
const finalPayload = {
start_date: payload.startDate,
end_date: payload.endDate
}
if (hasKey(payload, 'userId')) finalPayload.user_id = this.normalizeUserId(payload.userId)
const raw = ifPromise(payload, () => this._getEventCalendar(finalPayload))
return raw
.then(res => this.normalizeEventCalendar(res.data))
.catch(error => this.onError('getEventCalendar', error))
}
/**
* @typedef {object} EventOverview
* @property {number} gaTicketTotalSales - ga ticket total sales
* @property {number} vipTicketTotalSales - vip ticket total sales
* @property {number} gaTicketsSold - ga tickets sold count
* @property {number} vipTicketsSold - vip tickets sold count
* @property {number} gaTicketsCheckedInCount - ga tickets checked in count
* @property {number} vipTicketsCheckedInCount - vip tickets checked in count
* @property {number} gaTicketsAllocated - ga tickets capacity
* @property {number} vipTicketsAllocated - vip tickets capacity
*/
prototype.normalizeEventOverview = function (_data) {
const data = this.normalizeData(_data)
const mapToProps = {
ga_tickets_total_sales: 'gaTicketsTotalSales',
vip_tickets_total_sales: 'vipTicketsTotalSales',
ga_tickets_sold: 'gaTicketsSold',
vip_tickets_sold: 'vipTicketsSold',
ga_tickets_checked_in_count: 'gaTicketsCheckedIn',
vip_tickets_checked_in_count: 'vipTicketsCheckedIn',
ga_tickets_allocated: 'gaTicketsAllocated',
vip_tickets_allocated: 'vipTicketsAllocated'
}
const ret = this.filterAndMapProps(data, mapToProps)
ret.formattedGaTicketsTotalSales = formatCurrency(ret.gaTicketsTotalSales)
ret.formattedVipTicketsTotalSales = formatCurrency(ret.vipTicketsTotalSales)
return ret
}
prototype._getEventOverview = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/overview`),
method: 'GET',
requestType: 'json'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function getEventOverview
* @param {object} payload - The event payload
* @param {number} payload.id - event id
* @returns {EventOverview} - Event overview detail
*/
prototype.getEventOverview = function (payload) {
const finalPayload = {
id: payload.eventId
}
const raw = ifPromise(payload, () => this._getEventOverview(finalPayload))
return raw
.then(res => this.normalizeEventOverview(res.data))
.catch(error => this.onError('getEventOverview', error))
}
prototype._cloneEvent = function ({ userId, id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/events/${id}/clone`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function cloneEvent
* @param {object} payload - The event payload
* @param {string} [payload.userId='me'] - user to assign cloned event to
* @param {number} payload.id - event id
* @param {string} payload.title - title for cloned event
* @returns {Event} - new Event object
*/
prototype.cloneEvent = function (payload) {
const finalPayload = {
userId: this.normalizeUserId(payload.userId),
id: payload.id,
zevents_event: {
name: payload.title
}
}
const raw = ifPromise(payload, () => this._cloneEvent(finalPayload))
return raw
.then(res => this.normalizeEvent(res.data))
.catch(error => this.onError('cloneEvent', error))
}
prototype.normalizeEcheckPayment = function (payment, isSaveable = true) {
return {
echeck: {
account_holder_name: payment.echeck.accountHolderName,
routing_number: payment.echeck.routingNumber,
masked_bank_account_number: this.maskBankAccountNumber(payment.echeck.accountNumber),
encrypted_bank_account_number: this.encryptECheck(payment.echeck.accountNumber),
...(isSaveable ? { save_echeck: !!payment.echeck.saveEcheck } : {})
}
}
}
prototype.normalizeEcheckIdPayment = function (payment) {
return {
echeck_id: parseInt(payment.echeckId)
}
}
prototype.normalizeCreditCardIdPayment = function (payment) {
return {
card_id: parseInt(payment.cardId)
}
}
prototype.normalizeCreditCardPayment = function (payment, isSaveable = true) {
return {
credit_card: {
name: hasKey(payment.creditCard, 'nameOnCard') ? payment.creditCard.nameOnCard : payment.creditCard.name,
card_type: payment.creditCard.cardType,
address: payment.creditCard.address.line1,
city: payment.creditCard.address.city,
state: payment.creditCard.address.state,
zipcode: payment.creditCard.address.zipCode,
country: payment.creditCard.address.country || 'US',
encrypted_card_number: this.encryptCreditCardNumber(payment.creditCard.cardNumber),
gateway_cvv: payment.creditCard.ccv,
last_four: this.maskCreditCardNumber(payment.creditCard.cardNumber),
expiration_month: hasKey(payment.creditCard, 'expirationMonth') ? payment.creditCard.expirationMonth : payment.creditCard.expiration.split('/')[0],
expiration_year: hasKey(payment.creditCard, 'expirationYear') ? payment.creditCard.expirationYear : payment.creditCard.expiration.split('/')[1],
expiration_day: '01',
...(isSaveable ? { save_card: !!payment.creditCard.saveCard } : {})
}
}
}
prototype.normalizePayment = function (payment, isSaveable = true) {
if (hasKey(payment, 'cardId')) return this.normalizeCreditCardIdPayment(payment)
if (hasKey(payment, 'echeckId')) return this.normalizeEcheckIdPayment(payment)
if (hasKey(payment, 'echeck')) return this.normalizeEcheckPayment(payment, isSaveable)
if (hasKey(payment, 'creditCard')) return this.normalizeCreditCardPayment(payment, isSaveable)
return undefined
}
prototype._purchaseEventTickets = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/purchase`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
prototype.normalizeEventTicketPurchase = function (_data) {
const data = this.normalizeData(_data)
return data
// return this.normalizeEventGuestTicketEntry(_data)
}
/**
* @typedef {object} PaymentAddress
* @property {string} line1 - line1 of address
* @property {string} city - city
* @property {string} state - state
* @property {string} country - country
* @property {string} zipCode - zip code
*/
/**
* @typedef {object} PaymentEcheck
* @property {string} accountHolderName - name on account
* @property {string} routingNumber - routing number
* @property {boolean} saveEcheck - save eCheck on file?
*/
/**
* @typedef {object} PaymentCreditCard
* @property {string} name - name on card
* @property {string} cardNumber - cc number
* @property {string} ccv - security code
* @property {string} cardType - cc card type
* @property {number} expirationMonth - card exp month
* @property {number} expirationYear - card exp year
* @property {boolean} saveCard - save card on file?
* @property {PaymentAddress} address - address on card
*/
/**
* @typedef {object} Payment
* @property {number} [cardId] - credit card id on file to use
* @property {number} [echeckId] - echeck id on file to use
* @property {PaymentEcheck} [echeck] - new echeck details
* @property {PaymentCreditCard} [creditCard] - new echeck details
*/
/**
* @function purchaseEventTickets
* @param {object} payload - The payload
* @param {number} payload.id - event id
* @param {number} payload.generalTicketCount - how many general tickets
* @param {number} payload.vipTicketCount - how many vip tickets
* @param {number} payload.applyCredits - should kwivrr credit be applied
* @param {string} [payload.preferredLanguage] - if event is multi-lingual, which language would you prefer?
* @param {Payment} payload.payment - payment object
* @returns {void}
*/
prototype.purchaseEventTickets = function (payload) {
const {
id,
payment,
applyCredits,
vipTicketCount,
generalTicketCount
} = payload
const finalPayload = {
id: id,
apply_credits: !!applyCredits,
zevents_event_order: {
general_ticket_count: parseInt(generalTicketCount),
vip_ticket_count: parseInt(vipTicketCount)
},
payment: this.normalizePayment(payment)
}
if (hasKey(payload, 'preferredLanguage')) finalPayload.zevents_event_order.preferred_language = payload.preferredLanguage
const raw = ifPromise(payload, () => this._purchaseEventTickets(finalPayload))
return raw
.then(res => this.normalizeEventTicketPurchase(res.data))
.catch(error => {
console.log('##', error)
const mapErrors = {
'exceeds capacity': 'EVENT_TICKET_EXCEEDS_CAPACITY'
}
return this.onError('purchaseEventTickets', error, mapErrors)
})
}
prototype.normalizeRegisterForEvent = function (_data) {
const data = this.normalizeData(_data)
return data
}
prototype._registerForEvent = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/register`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
/**
* @function registerForEvent
* @param {object} payload - The event payload
* @param {string} payload.id - event id to register for
* @returns {void}
* @example
*
* registerForEvent({
* id: 11818
* })
*/
}
prototype.registerForEvent = function (payload) {
const {
id
} = payload
const finalPayload = {
id
}
const raw = ifPromise(payload, () => this._registerForEvent(finalPayload))
return raw
.then(res => this.normalizeEventTicketPurchase(res.data))
.catch(error => this.onError('registerForEvent', error))
}
prototype.normalizeUnregisterForEvent = function (_data) {
const data = this.normalizeData(_data)
return data
}
prototype._unregisterForEvent = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/unregister`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function unegisterForEvent
* @param {object} payload - The event payload
* @param {string} payload.id - event id to unregister for
* @returns {void}
* @example
*
* unregisterForEvent({
* id: 11818
* })
*/
prototype.unregisterForEvent = function (payload) {
const {
eventId
} = payload
const finalPayload = {
id: eventId
}
const raw = ifPromise(payload, () => this._unregisterForEvent(finalPayload))
return raw
.catch(error => this.onError('unregisterForEvent', error))
}
prototype._sellCustomEventTickets = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/sell_custom`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function sellCustomEventTickets
* @param {object} payload - The payload
* @param {number} payload.id - event id
* @param {number} payload.userId - user id to assign tickets
* @param {string} [payload.preferredLanguage] - if event is multi-lingual, which language would you prefer?
* @param {number} payload.generalTicketCount - how many general tickets
* @param {number} payload.generalTicketPrice - how much for general ticket?
* @param {number} [payload.vipTicketCount] - if vip tickets are sold, how many vip tickets
* @param {number} [payload.vipTicketPrice] - if vip tickets are sold, how much for vip ticket?
* @param {Payment} payload.payment - payment object
* @returns {void}
*/
prototype.sellCustomEventTickets = function (payload) {
const {
id,
userId,
payment,
generalTicketCount,
generalTicketPrice,
vipTicketCount,
vipTicketPrice
} = payload
const finalPayload = {
id: id,
apply_credits: false,
zevents_event_order: {
user_id: parseInt(userId),
general_ticket_count: parseInt(generalTicketCount),
general_price: generalTicketPrice || 0,
vip_ticket_count: parseInt(vipTicketCount),
vip_price: vipTicketPrice || 0
},
payment: this.normalizePayment(payment, false)
}
if (hasKey(payload, 'preferredLanguage')) finalPayload.zevents_event_order.preferred_language = payload.preferredLanguage
const raw = ifPromise(payload, () => this._sellCustomEventTickets(finalPayload))
return raw
.then(res => this.normalizeEventTicketPurchase(res.data))
.catch(error => this.onError('sellCustomEventTickets', error))
}
prototype._endEventStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/end_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function endEventStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to end
* @returns {void}
* @example
*
* endEventStream({
* id: 11818
* })
*/
prototype.endEventStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._endEventStream(finalPayload))
return raw
.catch(error => this.onError('endEventStream', error))
}
prototype._startEventStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/start_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function startEventStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to start
* @returns {void}
* @example
*
* startEventStream({
* id: 11818
* })
*/
prototype.startEventStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._startEventStream(finalPayload))
return raw
.catch(error => this.onError('startEventStream', error))
}
prototype._pauseEventStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/pause_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function pauseEventStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to pause
* @returns {void}
* @example
*
* pauseEventStream({
* id: 11818
* })
*/
prototype.pauseEventStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._pauseEventStream(finalPayload))
return raw
.catch(error => this.onError('pauseEventStream', error))
}
prototype._resumeEventStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/resume_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function resumeEventStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to pause
* @returns {void}
* @example
*
* resumeEventStream({
* id: 11818
* })
*/
prototype.resumeEventStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._resumeEventStream(finalPayload))
return raw
.catch(error => this.onError('resumeEventStream', error))
}
prototype._provisionStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/provision_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function provisionStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to start
* @returns {void}
* @example
*
* provisionStream({
* id: 11818
* })
*/
prototype.provisionStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._provisionStream(finalPayload))
return raw
.catch(error => this.onError('provisionStream', error))
}
prototype._deprovisionStream = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/deprovision_stream`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function deprovisionStream
* @param {object} payload - The event payload
* @param {string} payload.id - event id to start
* @returns {void}
* @example
*
* deprovisionStream({
* id: 11818
* })
*/
prototype.deprovisionStream = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._deprovisionStream(finalPayload))
return raw
.catch(error => this.onError('deprovisionStream', error))
}
/**
* @function registerForEvent
* @param {object} payload - The event payload
* @param {string} payload.id - event id to register for
* @returns {void}
* @example
*
* registerForEvent({
* id: 11818
* })
*/
prototype.registerForEvent = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._registerForEvent(finalPayload))
return raw
.catch(error => this.onError('registerForEvent', error))
}
prototype._unregisterForEvent = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/events/${id}/unregister`),
method: 'POST'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function unregisterForEvent
* @param {object} payload - The event payload
* @param {string} payload.id - event id to unregister for
* @returns {void}
* @example
*
* unregisterForEvent({
* id: 11818
* })
*/
prototype.unregisterForEvent = function (payload) {
const finalPayload = {
id: payload.id
}
const raw = ifPromise(payload, () => this._unregisterForEvent(finalPayload))
return raw
.catch(error => this.onError('unregisterForEvent', error))
}
prototype._getEventGuestTickets = function ({ event_id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/events/${event_id}/guest_tickets`),
method: 'GET',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
prototype.normalizeEventGuestTicketEntry = function (_data) {
const data = this.normalizeData(_data)
const ticketState = this.mapApiStatus(data.api_status)
const ret = {
giftedUserEmail: data?.gifted_user_email,
isCheckedIn: !!data.is_checked_in,
ticketState,
currentVipPrice: normalizeCurrency(data.event.vip_ticket_price),
currentGeneralPrice: normalizeCurrency(data.event.general_ticket_price),
id: parseInt(data.id),
orderId: parseInt(data.order_id),
currentTicketHolder: data.ticket_holder.email,
userName: `${data.buyer.firstname} ${data.buyer.lastname}`,
userEmail: data.buyer.email,
refund: 0,
credit: 0,
ticketType: data.type,
qrCode: data.qr_code
}
const hasSourceable = hasKey(data.event_order, 'payment_source') && data.event_order.payment_source?.sourceable?.id
if (data?.event_order) {
ret.chargeInfo = {
individualCharges: [
{
id: hasSourceable ? data.event_order.payment_source.sourceable.id : -1,
ticketType: data.type,
numTickets: parseInt(data.event_order.event_tickets.length),
ticketTotal: normalizeCurrency(data.event_order.sub_total),
formattedTicketTotal: formatCurrency(normalizeCurrency(data.event_order.sub_total)),
subtotal: normalizeCurrency(data.event_order.sub_total),
formattedSubtotal: formatCurrency(normalizeCurrency(data.event_order.sub_total)),
serviceFee: normalizeCurrency(data.event_order.service_fee),
formattedServiceFee: formatCurrency(normalizeCurrency(data.event_order.service_fee)),
tax: normalizeCurrency(data.event_order.tax_total),
formattedTax: formatCurrency(normalizeCurrency(data.event_order.tax_total)),
total: normalizeCurrency(data.event_order.total_price),
formattedTotal: formatCurrency(normalizeCurrency(data.event_order.total_price)),
paymentDetails: {
nameOnCard: hasSourceable ? data.event_order.payment_source.sourceable.name_on_card : `${data.buyer.firstname} ${data.buyer.lastname}`,
lastFourDigits: hasSourceable ? data.event_order.payment_source.sourceable.last_four : '(credit applied)'
}
}
]
}
}
ret.isGiftedToMe = !data?.event_order
ret.isVip = ret.ticketType === 'vip'
ret.isGeneral = !ret.isVip
ret.hasVip = ret.currentVipPrice > 0
ret.isActive = this.isActiveTicket(ret)
ret.isTransferred = this.isTransferredTicket(ret)
ret.isCheckInable = this.isCheckInableTicket(ret)
ret.isUncheckInable = this.isUncheckInableTicket(ret)
ret.isTransferrable = this.isTransferrableTicket(ret)
ret.isReclaimable = this.isReclaimableTicket(ret)
ret.isSetCredentialsable = this.isSetCredentialsableTicket(ret)
ret.isRevokable = this.isRevokableTicket(ret)
ret.isPendingTransfer = this.isPendingTransferTicket(ret)
ret.isRefunded = this.isRefundedTicket(ret)
ret.isParticallyRefunded = this.isPartiallyRefundedTicket(ret)
ret.isRevoked = this.isRevokedTicket(ret)
ret.isUpgradeable = this.isUpgradeableTicket(ret)
console.log('##', data.event)
ret.event = this.normalizeEvent(data.event)
ret.isCheckInTime = ret.event.isCheckInTime
return ret
}
prototype.getEventGuestTickets = function (payload) {
const finalPayload = {
event_id: payload.eventId,
page: payload.page || 1
}
if (payload.term) finalPayload.term = payload.term
const raw = ifPromise(payload, () => this._getEventGuestTickets(finalPayload))
return raw
.then(res => {
return this.normalizeListData(res.data, this.normalizeEventGuestTicketEntry)
})
// .catch(error => {
// console.log('##error', error)
// return this.onError('getEventGuestTickets', error)
// })
}
}
export default ZEventsApiEvent