import ifPromise from 'onyx-common/ifPromise'
import hasKey from 'onyx-common/hasKey'
const ZEventsApiUser = ({ prototype }) => {
prototype.normalizeUser = function (_data) {
const data = this.normalizeData(_data)
data.email_visibility = !!data.email_visibility
if (!hasKey(data, 'user')) data.user = {}
if (!hasKey(data.user, 'privacy_setting')) {
data.user.privacy_setting = {
show_followees: true,
show_attending: true,
show_attended: true,
show_in_user_search: true,
show_in_speaker_search: true
}
}
const mapProps = {
user_id: ['id', val => parseInt(val)],
avatar_url: 'avatarUrl',
banner_url: 'bannerUrl',
email: 'email',
unverified_email: 'unverifiedEmail',
is_email_verified: 'isEmailVerified',
phone: 'phone',
email_visibility: ['showEmail', val => !!val],
pinterest: 'pinterest',
instagram: 'instagram',
facebook: 'facebook',
twitter: 'twitter',
shop_link: 'shopUrl',
tagline: 'tagLine',
firstname: 'firstName',
lastname: 'lastName',
has_avatar_image: ['hasAvatarImage', val => !!val],
has_banner_image: ['hasBannerImage', val => !!val],
share_url: 'shareUrl',
is_current_user: ['isCurrentUser', val => !!val],
username: 'username',
is_followed: ['isFollowed', val => !!val],
follower_count: ['followerCount', val => parseInt(val) || 0]
}
const ret = this.filterAndMapProps(data, mapProps)
ret.isFollowed = ret.isFollowed || false
ret.followerCount = ret.followerCount || 0
// privacy settings
ret.showFollowees = !!data.user.privacy_setting?.show_followees
ret.showEventsAttending = !!data.user.privacy_setting?.show_events_attending
ret.showEventsAttended = !!data.user.privacy_setting?.show_events_attended
ret.showInUserSearch = !!data.user.privacy_setting?.show_in_user_search
ret.showInSpeakerSearch = !!data.user.privacy_setting?.show_in_speaker_search
if (!hasKey(ret, 'isCurrentUser')) ret.isCurrentUser = false
return ret
}
prototype.normalizeUserProfile = prototype.normalizeUser
prototype._createUser = function (_data) {
const payload = {
url: this.getUrl('/api/v1/users'),
method: 'POST',
requestType: 'json',
data: {
..._data,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} CreateUserReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {number} expiresIn - seconds until accessToken is expired
* @property {number} createdAt - when account was created
*/
/**
* @function createUser
* @param {object} payload - The user payload
* @param {string} payload.firstName - first name of user
* @param {string} payload.lastName - last name of user
* @param {string} payload.email - email address of user
* @param {string} payload.password - password for user
* @param {boolean} [payload.withLogin=true] - should we also log the new user in?
* @returns {CreateUserReturn} - loggedIn user
* @example
*
* createUser({
* firstName: 'Joe',
* lastName: 'Blow',
* email: 'joe@foo.com',
* password: '1234'
* })
*/
prototype.createUser = function (payload) {
const finalPayload = {
firstname: payload.firstName,
lastname: payload.lastName,
email: payload.email,
password: payload.password
}
if (!hasKey(payload, 'withLogin')) payload.withLogin = true
const withLogin = Boolean(payload.withLogin)
const raw = ifPromise(payload, () => this._createUser(finalPayload))
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => {
if (withLogin) return this.manualLogin(data)
return data
})
.catch(res => {
const mapError = {
'Email Adress has already been taken': 'CREATE_USER_EMAIL_ADDRESS_TAKEN',
'Password is too short (minimum is 6 characters)': 'CREATE_USER_PASSWORD_TOO_SHORT'
}
return this.onError('createUser', res, mapError)
})
}
prototype._getUserAccount = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/show`),
method: 'GET'
}
return this.authenticatedFetchWrap(payload)
}
/**
* @typedef {object} getUserAccountReturn
* @property {number} id - user id
* @property {string} firstName - first name
* @property {string} lastName - last name
* @property {string} tagLine - tag line for use in profile
* @property {string} facebook - facebook handle
* @property {string} instagram - instagram handle
* @property {string} pinterest - pinterest handle
* @property {string} linkedin - linkedin handle
* @property {string} twitter - twitter handle
* @property {string} avatarUrl - image url for avatar
* @property {string} bannerUrl - image url for banner
* @property {string} shopUrl - url for shop link
* @property {string} email - email address
* @property {string} phone - phone number
* @property {boolean} hasAvatarImage - did the user upload an avatar image?
* @property {boolean} hasBannerImage - did the user upload a banner image?
* @property {boolean} showEmail - should email display on user's profile? If this is false, then `email` will also be empty (unless requester is self or admin)
* @property {boolean} showFollowees - show followees?
* @property {boolean} showEventsAttended - show events attended?
* @property {boolean} showEventsAttending - show events user is attending?
* @property {boolean} showInUserSearch - show profile in user search?
* @property {boolean} showInSpeakerSearch - show profile in speaker search?
* @property {string} shareUrl - url to share profile
*/
/**
* @function getUserAccount
* @param {object} payload - The user payload
* @param {string} [payload.id='me'] - ID of user
* @returns {getUserAccountReturn}
* @example
* getUserAccount()
*/
prototype.getUserAccount = function (payload = {}) {
const {
id
} = payload
const finalPayload = {
id: this.normalizeUserId(id)
}
const raw = ifPromise(payload, () => this._getUserAccount(finalPayload))
return raw
.then(res => this.normalizeUser(res.data))
.catch(error => this.onError('getUserAccount', error))
}
prototype._getUserProfile = function ({ id }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/profile`),
method: 'GET'
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @typedef {object} getUserProfileReturn
* @property {number} id - user id
* @property {string} firstName - first name
* @property {string} lastName - last name
* @property {string} tagLine - tag line for use in profile
* @property {string} facebook - facebook handle
* @property {string} instagram - instagram handle
* @property {string} pinterest - pinterest handle
* @property {string} linkedin - linkedin handle
* @property {string} twitter - twitter handle
* @property {string} avatarUrl - image url for avatar
* @property {string} bannerUrl - image url for banner
* @property {string} shopUrl - url for shop link
* @property {string} email - email address
* @property {string} phone - phone number
* @property {boolean} showEmail - should email display on user's profile? If this is false, then `email` will also be empty (unless requester is self or admin)
*/
/**
* @function getUserProfile
* @param {object} payload - The user payload
* @param {number} payload.id - ID of user
* @returns {getUserProfileReturn}
* @example
* getUserAccount({
* id: 123
* })
*/
prototype.getUserProfile = function (payload) {
const {
id
} = payload
const finalPayload = {
id: this.normalizeUserId(id)
}
const raw = ifPromise(payload, () => this._getUserProfile(finalPayload))
return raw
.then(res => this.normalizeUserProfile(res.data))
.catch(error => this.onError('getUserProfile', error))
}
prototype._resetPasswordConfirm = function (data) {
const payload = {
url: this.getUrl('/api/v1/users/reset_password_confirm'),
method: 'POST',
requestType: 'json',
data: {
...data,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} ResetPasswordConfirmReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {number} expiresIn - seconds until accessToken is expired
* @property {number} createdAt - when account was created
*/
/**
* @function resetPasswordConfirm
* @param {object} payload - The payload object
* @param {string} payload.resetPasswordToken - token to reset password
* @param {string} payload.password - password for login
* @param {string} [payload.withLogin=true] - should we login this user after reset?
* @returns {ResetPasswordConfirmReturn} - reset password return
* @example
* resetPasswordConfirm({
* resetPasswordToken: 'asdfasdfasfdfasdf',
* password: 'foo'
* })
*/
prototype.resetPasswordConfirm = function (payload) {
const mapProps = {
resetPasswordToken: 'reset_password_token',
password: 'password'
}
const finalPayload = this.filterAndMapProps(payload, mapProps)
if (!hasKey(payload, 'withLogin')) payload.withLogin = true
const withLogin = Boolean(payload.withLogin)
const raw = ifPromise(payload, () => this._resetPasswordConfirm(finalPayload))
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => {
if (withLogin) return this.manualLogin(data)
return data
})
.catch(error => this.onError('resetPasswordConfirm', error))
}
prototype._resetPasswordInit = function ({ ...rest }) {
const payload = {
url: this.getUrl('/api/v1/users/reset_password_init'),
method: 'POST',
requestType: 'json',
data: rest
}
return this.fetchWrap(payload)
}
/**
* @function resetPasswordInit
* @param {object} payload - The payload object
* @param {string} payload.email - email to reset
* @returns {void}
* @example
* resetPasswordInit({
* email: 'jim@bob.com'
* })
*/
prototype.resetPasswordInit = function (payload) {
const finalPayload = {
email: payload.email
}
const raw = ifPromise(payload, () => this._resetPasswordInit(finalPayload))
return raw
.catch(error => this.onError('resetPasswordInit', error))
}
prototype.normalizeSearchUserEntry = function (_data) {
const data = this.normalizeData(_data)
const ret = {
userId: data.profile.user_id,
firstName: data.firstname,
lastName: data.lastname,
tagLine: data.profile.tagline,
avatarUrl: data.profile.avatar_url
}
return ret
}
prototype._searchUsers = function (data) {
const payload = {
url: this.getUrl('/api/v1/users'),
method: 'GET',
requestType: 'json',
data
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @typedef {object} searchUsersListSummary
* @property {number} page - page of results
* @property {number} perPage - results per page
* @property {boolean} hasMore - are more pages available?
*/
/**
* @typedef {object} searchUsersCriteriaSummary
* @property {number} page - page of results
* @property {string} term - term searched for
*/
/**
* @typedef {object} searchUsersEntry
* @property {number} userId - user id
* @property {string} firstName - user's first name
* @property {string} lastName - user's last name
* @property {string} tagLine - user's tag line
* @property {string} avatarUrl - users's avatar url
*/
/**
* @typedef {object} searchUsersReturn
* @property {searchUsersEntry[]} users - list of users returned
* @property {searchUsersListSummary} listSummary - list summary object
* @property {searchUsersCriteriaSummary} criteriaSummary - criteria summary object
*/
/**
* @function searchUsers
* @param {object} payload - The payload object
* @param {string} [payload.term] - search across user's first name, last name and email
* @param {string} [payload.page=1] - search across user's first name, last name and email
* @returns {searchUsersReturn} searchUsersReturn
* @example
* searchUsers({
* term: 'bonnie'
* })
*/
prototype.searchUsers = function (payload = {}) {
const finalPayload = {
search: payload?.term,
page: payload?.page || 1
}
const raw = ifPromise(payload, () => this._searchUsers(finalPayload))
return raw
.then(res => this.normalizeListData(res.data, this.normalizeSearchUserEntry))
.catch(error => this.onError('searchUsersError', error))
}
prototype._updateUserAccount = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/update`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function updateUserAccount
* @description All parameters are optional and can be updated independently of others
* @param {object} payload - The user payload
* @param {number|string} [payload.id='me'] - ID of user
* @param {string} [payload.firstName] - first name
* @param {string} [payload.lastName] - last name
* @param {string} [payload.tagLine] - tag line
* @param {string} [payload.facebook] - facebook handle
* @param {string} [payload.instagram] - instagram handle
* @param {string} [payload.pinterest] - last name
* @param {string} [payload.linkedin] - last name
* @param {string} [payload.twitter] - last name
* @param {string} [payload.avatarUrl] - avatar url
* @param {string} [payload.shopUrl] - shop url
* @param {string} [payload.avatarBase64] - avatar base64 data
* @param {string} [payload.bannerUrl] - banner url
* @param {string} [payload.bannerBase64] - banner base64 data
* @param {string} [payload.phone] - phone number
* @param {boolean} [payload.showEmail] - should email be displayed on profile?
* @returns {void}
* @example
* updateUserAccount({
* id: 123,
* firstName: 'Test',
* lastName; 'User'
* })
*/
prototype.updateUserAccount = function (payload) {
const {
id,
...rest
} = payload
if (hasKey(rest, 'showEmail')) rest.showEmail = !!rest.showEmail
const mapProps = {
avatarUrl: 'avatar_url',
avatarBase64: 'avatar_base64',
bannerUrl: 'banner_url',
bannerBase64: 'banner_base64',
shopUrl: 'shop_link',
phone: 'phone',
facebook: 'facebook',
instagram: 'instagram',
twitter: 'twitter',
pinterest: 'pinterest',
tagLine: 'tagline',
linkedin: 'linkedin',
firstName: 'firstname',
lastName: 'lastname',
showEmail: 'email_visibility'
}
const mappedData = this.filterAndMapProps(rest, mapProps)
const finalPayload = {
...mappedData,
id: this.normalizeUserId(id)
}
if (hasKey(finalPayload, 'phone')) finalPayload.phone_visibility = (finalPayload.phone.length > 0)
const raw = ifPromise(finalPayload, () => this._updateUserAccount(finalPayload))
return raw
.then(res => Promise.resolve(this.normalizeAndCamelize(res)))
.catch(error => this.onError('updateUserAccount', error))
}
prototype._updateUserEmail = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/email`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function updateUserEmail
* @param {object} payload - The user payload
* @param {number|string} [payload.id='me'] - ID of user
* @param {string} payload.email - email address
* @returns {void}
* @example
* updateUserEmail({
* id: 123,
* email: 'foo@bar.com'
* })
*/
prototype.updateUserEmail = function (payload) {
const {
id,
email
} = payload
const finalPayload = {
email,
id: this.normalizeUserId(id)
}
const raw = ifPromise(payload, () => this._updateUserEmail(finalPayload))
return raw
.catch(error => {
const mapError = {
'Email is already in use': 'UPDATE_USER_EMAIL_ALREADY_IN_USE'
}
return this.onError('updateUserEmail', error, mapError)
})
}
prototype._resendUserEmail = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/resend_verification_email`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function resendUserEmail
* @param {object} payload - The user payload
* @param {number|string} [payload.id='me'] - ID of user
* @returns {void}
* @example
* resendUserEmail({
* id: 123,
* })
*/
prototype.resendUserEmail = function (payload) {
const {
id
} = payload
const finalPayload = {
id: this.normalizeUserId(id)
}
const raw = ifPromise(payload, () => this._resendUserEmail(finalPayload))
return raw
.catch(error => this.onError('resendUserEmail', error))
}
prototype._updateUserUsername = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/username`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function updateUserUsername
* @param {object} payload - The user payload
* @param {number|string} [payload.id='me'] - ID of user
* @param {string} payload.username - username
* @returns {void}
* @example
* updateUserUsername({
* id: 123,
* username: 'user123'
* })
*/
prototype.updateUserUsername = function (payload) {
const {
id,
username
} = payload
const finalPayload = {
username,
id: this.normalizeUserId(id)
}
const raw = ifPromise(payload, () => this._updateUserUsername(finalPayload))
return raw
.catch(error => {
const mapError = {
'Username is already in use': 'UPDATE_USER_USERNAME_ALREADY_IN_USE'
}
return this.onError('updateUserUsername', error, mapError)
})
}
prototype._updateUserPassword = function ({ id, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${id}/account/password`),
method: 'PUT',
requestType: 'json',
data: rest
}
return this.authenticatedFetchWrap(payload)
}
/**
* @function updateUserPassword
* @description currentPassword is required to validate user is allowed to change password
* @param {object} payload - The user payload
* @param {number|string} [payload.id='me'] - ID of user
* @param {string} payload.newPassword - new password
* @param {string} payload.currentPassword - current password
* @returns {void}
* @example
* updateUserPassword({
* newPassword: 'moonbase'
* currentPassword: 'oaktree'
* })
*/
prototype.updateUserPassword = function (payload) {
const mapProps = {
newPassword: 'new_password',
currentPassword: 'current_password',
id: 'id'
}
const finalPayload = this.filterAndMapProps(payload, mapProps)
finalPayload.id = this.normalizeUserId(finalPayload.id)
const raw = ifPromise(payload, () => this._updateUserPassword(finalPayload))
return raw
.catch(error => {
const mapError = {
'Email is already in use': 'UPDATE_USER_EMAIL_ALREADY_IN_USE'
}
return this.onError('updateUserPassword', error, mapError)
})
}
prototype._verifyEmail = function (data) {
const payload = {
url: this.getUrl('/api/v1/users/account/email/verify'),
method: 'POST',
requestType: 'json',
data: {
...data,
...this.getAuthParams()
}
}
return this.fetchWrap(payload)
}
/**
* @typedef {object} VerifyEmailReturn
* @property {string} accessToken - access token
* @property {string} refreshToken - refresh token
* @property {string} expiresIn - expires in
* @property {string} createdAt - created at
*/
/**
* @function verifyEmail
* @param {object} payload - The verifyEmail payload
* @param {string} payload.confirmationToken - change confirmation token
* @param {boolean} [payload.withLogin=true] - should we also log the user in?
* @returns {VerifyEmailReturn} - VerifyEmailReturn
* @example
* verifyEmail({
* confirmationToken: 'asdfasdfasdfdsafsdafdsafdsafdsf'
* })
*/
prototype.verifyEmail = function (payload) {
const finalPayload = {
confirmation_token: payload.confirmationToken
}
if (!hasKey(payload, 'withLogin')) payload.withLogin = true
const withLogin = Boolean(payload.withLogin)
const raw = ifPromise(payload, () => this._verifyEmail(finalPayload))
return raw
.then(res => this.normalizeLoginableReturn(res.data))
.then(data => {
if (withLogin) return this.manualLogin(data)
return data
})
.catch(error => this.onError('verifyEmail', error))
}
prototype._registerForPushNotifications = function (data) {
const payload = {
url: this.getUrl('/api/v1/fcm_notifications/register'),
method: 'POST',
requestType: 'json',
data
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @function registerForPushNotifications
* @param {object} payload - The verifyEmail payload
* @param {string} payload.token - expo push token
* @param {string} payload.platform - enum(ios, android)
* @param {string} payload.deviceDetails - JSON.stringify'd info for device details
* @returns {void}
* @example
*
* registerForPushNotifications({
* token: 'asdfasdfasdfdsafsdafdsafdsafdsf',
* platform: 'ios',
* deviceDetails: '{\"foo\": \"bar\"}'
* })
*/
prototype.registerForPushNotifications = function (payload) {
const finalPayload = {
token: payload.token,
platform: payload.platform,
device_details: JSON.stringify(payload.deviceDetails)
}
const raw = ifPromise(payload, () => this._registerForPushNotifications(finalPayload))
return raw
.catch(error => this.onError('registerForPushNotifications', error))
}
prototype._follow = function ({ userId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/follow`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @function follow
* @param {object} payload - The user payload
* @param {string} payload.userId - user's id
* @returns {void}
* @example
*
* follow({
* userId: 1948
* })
*/
prototype.follow = function (payload) {
const finalPayload = {
userId: payload.userId
}
const raw = ifPromise(payload, () => this._follow(finalPayload))
return raw
.then(res => this.normalizeUser(res.data))
.catch(error => this.onError('follow', error))
}
prototype._unfollow = function ({ userId, ...rest }) {
const payload = {
url: this.getUrl(`/api/v1/users/${userId}/unfollow`),
method: 'POST',
requestType: 'json',
data: rest
}
return this.optionallyAuthenticatedFetchWrap(payload)
}
/**
* @function unfollow
* @param {object} payload - The verifyEmail payload
* @param {string} payload.token - expo push token
* @param {string} payload.platform - enum(ios, android)
* @param {string} payload.deviceDetails - JSON.stringify'd info for device details
* @returns {void}
* @example
*
* unfollow({
* token: 'asdfasdfasdfdsafsdafdsafdsafdsf',
* platform: 'ios',
* deviceDetails: '{\"foo\": \"bar\"}'
* })
*/
prototype.unfollow = function (payload) {
const finalPayload = {
userId: payload.userId
}
const raw = ifPromise(payload, () => this._unfollow(finalPayload))
return raw
.catch(error => this.onError('unfollow', error))
}
}
export default ZEventsApiUser