Referrals bug fix and attribute group
This commit is contained in:
parent
db537a97ba
commit
39c38a669e
|
@ -63,3 +63,4 @@ export type notification_reason_types =
|
||||||
| 'on_group_you_are_member_of'
|
| 'on_group_you_are_member_of'
|
||||||
| 'tip_received'
|
| 'tip_received'
|
||||||
| 'bet_fill'
|
| 'bet_fill'
|
||||||
|
| 'user_joined_from_your_group_invite'
|
||||||
|
|
|
@ -38,6 +38,7 @@ export type User = {
|
||||||
|
|
||||||
referredByUserId?: string
|
referredByUserId?: string
|
||||||
referredByContractId?: string
|
referredByContractId?: string
|
||||||
|
referredByGroupId?: string
|
||||||
lastPingTime?: number
|
lastPingTime?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ service cloud.firestore {
|
||||||
allow read;
|
allow read;
|
||||||
allow update: if resource.data.id == request.auth.uid
|
allow update: if resource.data.id == request.auth.uid
|
||||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||||
.hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'referredByContractId', 'lastPingTime']);
|
.hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime']);
|
||||||
// User referral rules
|
// User referral rules
|
||||||
allow update: if resource.data.id == request.auth.uid
|
allow update: if resource.data.id == request.auth.uid
|
||||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||||
.hasOnly(['referredByUserId'])
|
.hasOnly(['referredByUserId', 'referredByContractId', 'referredByGroupId'])
|
||||||
// only one referral allowed per user
|
// only one referral allowed per user
|
||||||
&& !("referredByUserId" in resource.data)
|
&& !("referredByUserId" in resource.data)
|
||||||
// user can't refer themselves
|
// user can't refer themselves
|
||||||
|
|
|
@ -253,20 +253,6 @@ export const createNotification = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyUserReceivedReferralBonus = async (
|
|
||||||
userToReasonTexts: user_to_reason_texts,
|
|
||||||
relatedUserId: string
|
|
||||||
) => {
|
|
||||||
if (shouldGetNotification(relatedUserId, userToReasonTexts))
|
|
||||||
userToReasonTexts[relatedUserId] = {
|
|
||||||
// If the referrer is the market creator, just tell them they joined to bet on their market
|
|
||||||
reason:
|
|
||||||
sourceContract?.creatorId === relatedUserId
|
|
||||||
? 'user_joined_to_bet_on_your_market'
|
|
||||||
: 'you_referred_user',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const notifyContractCreatorOfUniqueBettorsBonus = async (
|
const notifyContractCreatorOfUniqueBettorsBonus = async (
|
||||||
userToReasonTexts: user_to_reason_texts,
|
userToReasonTexts: user_to_reason_texts,
|
||||||
userId: string
|
userId: string
|
||||||
|
@ -284,8 +270,6 @@ export const createNotification = async (
|
||||||
} else if (sourceType === 'group' && relatedUserId) {
|
} else if (sourceType === 'group' && relatedUserId) {
|
||||||
if (sourceUpdateType === 'created')
|
if (sourceUpdateType === 'created')
|
||||||
await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
|
await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
|
||||||
} else if (sourceType === 'user' && relatedUserId) {
|
|
||||||
await notifyUserReceivedReferralBonus(userToReasonTexts, relatedUserId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following functions need sourceContract to be defined.
|
// The following functions need sourceContract to be defined.
|
||||||
|
@ -435,3 +419,52 @@ export const createGroupCommentNotification = async (
|
||||||
}
|
}
|
||||||
await notificationRef.set(removeUndefinedProps(notification))
|
await notificationRef.set(removeUndefinedProps(notification))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createReferralNotification = async (
|
||||||
|
toUser: User,
|
||||||
|
referredUser: User,
|
||||||
|
idempotencyKey: string,
|
||||||
|
bonusAmount: string,
|
||||||
|
referredByContract?: Contract,
|
||||||
|
referredByGroup?: Group
|
||||||
|
) => {
|
||||||
|
const notificationRef = firestore
|
||||||
|
.collection(`/users/${toUser.id}/notifications`)
|
||||||
|
.doc(idempotencyKey)
|
||||||
|
const notification: Notification = {
|
||||||
|
id: idempotencyKey,
|
||||||
|
userId: toUser.id,
|
||||||
|
reason: referredByGroup
|
||||||
|
? 'user_joined_from_your_group_invite'
|
||||||
|
: referredByContract?.creatorId === toUser.id
|
||||||
|
? 'user_joined_to_bet_on_your_market'
|
||||||
|
: 'you_referred_user',
|
||||||
|
createdTime: Date.now(),
|
||||||
|
isSeen: false,
|
||||||
|
sourceId: referredUser.id,
|
||||||
|
sourceType: 'user',
|
||||||
|
sourceUpdateType: 'updated',
|
||||||
|
sourceContractId: referredByContract?.id,
|
||||||
|
sourceUserName: referredUser.name,
|
||||||
|
sourceUserUsername: referredUser.username,
|
||||||
|
sourceUserAvatarUrl: referredUser.avatarUrl,
|
||||||
|
sourceText: bonusAmount,
|
||||||
|
// Only pass the contract referral details if they weren't referred to a group
|
||||||
|
sourceContractCreatorUsername: !referredByGroup
|
||||||
|
? referredByContract?.creatorUsername
|
||||||
|
: undefined,
|
||||||
|
sourceContractTitle: !referredByGroup
|
||||||
|
? referredByContract?.question
|
||||||
|
: undefined,
|
||||||
|
sourceContractSlug: !referredByGroup ? referredByContract?.slug : undefined,
|
||||||
|
sourceSlug: referredByGroup
|
||||||
|
? groupPath(referredByGroup.slug)
|
||||||
|
: referredByContract?.slug,
|
||||||
|
sourceTitle: referredByGroup
|
||||||
|
? referredByGroup.name
|
||||||
|
: referredByContract?.question,
|
||||||
|
}
|
||||||
|
await notificationRef.set(removeUndefinedProps(notification))
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupPath = (groupSlug: string) => `/group/${groupSlug}`
|
||||||
|
|
|
@ -2,11 +2,12 @@ import * as functions from 'firebase-functions'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import { REFERRAL_AMOUNT, User } from '../../common/user'
|
import { REFERRAL_AMOUNT, User } from '../../common/user'
|
||||||
import { HOUSE_LIQUIDITY_PROVIDER_ID } from '../../common/antes'
|
import { HOUSE_LIQUIDITY_PROVIDER_ID } from '../../common/antes'
|
||||||
import { createNotification } from './create-notification'
|
import { createReferralNotification } from './create-notification'
|
||||||
import { ReferralTxn } from '../../common/txn'
|
import { ReferralTxn } from '../../common/txn'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { LimitBet } from 'common/bet'
|
import { LimitBet } from 'common/bet'
|
||||||
import { QuerySnapshot } from 'firebase-admin/firestore'
|
import { QuerySnapshot } from 'firebase-admin/firestore'
|
||||||
|
import { Group } from 'common/group'
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
|
||||||
export const onUpdateUser = functions.firestore
|
export const onUpdateUser = functions.firestore
|
||||||
|
@ -54,6 +55,17 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
|
||||||
}
|
}
|
||||||
console.log(`referredByContract: ${referredByContract}`)
|
console.log(`referredByContract: ${referredByContract}`)
|
||||||
|
|
||||||
|
let referredByGroup: Group | undefined = undefined
|
||||||
|
if (user.referredByGroupId) {
|
||||||
|
const referredByGroupDoc = firestore.doc(
|
||||||
|
`groups/${user.referredByGroupId}`
|
||||||
|
)
|
||||||
|
referredByGroup = await transaction
|
||||||
|
.get(referredByGroupDoc)
|
||||||
|
.then((snap) => snap.data() as Group)
|
||||||
|
}
|
||||||
|
console.log(`referredByGroup: ${referredByGroup}`)
|
||||||
|
|
||||||
const txns = (
|
const txns = (
|
||||||
await firestore
|
await firestore
|
||||||
.collection('txns')
|
.collection('txns')
|
||||||
|
@ -100,18 +112,13 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
|
||||||
totalDeposits: referredByUser.totalDeposits + REFERRAL_AMOUNT,
|
totalDeposits: referredByUser.totalDeposits + REFERRAL_AMOUNT,
|
||||||
})
|
})
|
||||||
|
|
||||||
await createNotification(
|
await createReferralNotification(
|
||||||
user.id,
|
referredByUser,
|
||||||
'user',
|
|
||||||
'updated',
|
|
||||||
user,
|
user,
|
||||||
eventId,
|
eventId,
|
||||||
txn.amount.toString(),
|
txn.amount.toString(),
|
||||||
referredByContract,
|
referredByContract,
|
||||||
'user',
|
referredByGroup
|
||||||
referredByUser.id,
|
|
||||||
referredByContract?.slug,
|
|
||||||
referredByContract?.question
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,11 +89,11 @@ export async function getGroupsWithContractId(
|
||||||
setGroups(await getValues<Group>(q))
|
setGroups(await getValues<Group>(q))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addUserToGroupViaSlug(groupSlug: string, userId: string) {
|
export async function addUserToGroupViaId(groupId: string, userId: string) {
|
||||||
// get group to get the member ids
|
// get group to get the member ids
|
||||||
const group = await getGroupBySlug(groupSlug)
|
const group = await getGroup(groupId)
|
||||||
if (!group) {
|
if (!group) {
|
||||||
console.error(`Group not found: ${groupSlug}`)
|
console.error(`Group not found: ${groupId}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return await joinGroup(group, userId)
|
return await joinGroup(group, userId)
|
||||||
|
|
|
@ -35,7 +35,7 @@ import { feed } from 'common/feed'
|
||||||
import { CATEGORY_LIST } from 'common/categories'
|
import { CATEGORY_LIST } from 'common/categories'
|
||||||
import { safeLocalStorage } from '../util/local'
|
import { safeLocalStorage } from '../util/local'
|
||||||
import { filterDefined } from 'common/util/array'
|
import { filterDefined } from 'common/util/array'
|
||||||
import { addUserToGroupViaSlug } from 'web/lib/firebase/groups'
|
import { addUserToGroupViaId } from 'web/lib/firebase/groups'
|
||||||
import { removeUndefinedProps } from 'common/util/object'
|
import { removeUndefinedProps } from 'common/util/object'
|
||||||
import { randomString } from 'common/util/random'
|
import { randomString } from 'common/util/random'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
@ -99,13 +99,13 @@ export function listenForPrivateUser(
|
||||||
const CACHED_USER_KEY = 'CACHED_USER_KEY'
|
const CACHED_USER_KEY = 'CACHED_USER_KEY'
|
||||||
const CACHED_REFERRAL_USERNAME_KEY = 'CACHED_REFERRAL_KEY'
|
const CACHED_REFERRAL_USERNAME_KEY = 'CACHED_REFERRAL_KEY'
|
||||||
const CACHED_REFERRAL_CONTRACT_ID_KEY = 'CACHED_REFERRAL_CONTRACT_KEY'
|
const CACHED_REFERRAL_CONTRACT_ID_KEY = 'CACHED_REFERRAL_CONTRACT_KEY'
|
||||||
const CACHED_REFERRAL_GROUP_SLUG_KEY = 'CACHED_REFERRAL_GROUP_KEY'
|
const CACHED_REFERRAL_GROUP_ID_KEY = 'CACHED_REFERRAL_GROUP_KEY'
|
||||||
|
|
||||||
export function writeReferralInfo(
|
export function writeReferralInfo(
|
||||||
defaultReferrerUsername: string,
|
defaultReferrerUsername: string,
|
||||||
contractId?: string,
|
contractId?: string,
|
||||||
referralUsername?: string,
|
referralUsername?: string,
|
||||||
groupSlug?: string
|
groupId?: string
|
||||||
) {
|
) {
|
||||||
const local = safeLocalStorage()
|
const local = safeLocalStorage()
|
||||||
const cachedReferralUser = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
|
const cachedReferralUser = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
|
||||||
|
@ -121,7 +121,7 @@ export function writeReferralInfo(
|
||||||
local?.setItem(CACHED_REFERRAL_USERNAME_KEY, referralUsername)
|
local?.setItem(CACHED_REFERRAL_USERNAME_KEY, referralUsername)
|
||||||
|
|
||||||
// Always write the most recent explicit group invite query value
|
// Always write the most recent explicit group invite query value
|
||||||
if (groupSlug) local?.setItem(CACHED_REFERRAL_GROUP_SLUG_KEY, groupSlug)
|
if (groupId) local?.setItem(CACHED_REFERRAL_GROUP_ID_KEY, groupId)
|
||||||
|
|
||||||
// Write the first contract id that we see.
|
// Write the first contract id that we see.
|
||||||
const cachedReferralContract = local?.getItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
|
const cachedReferralContract = local?.getItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
|
||||||
|
@ -134,14 +134,14 @@ async function setCachedReferralInfoForUser(user: User | null) {
|
||||||
// if the user wasn't created in the last minute, don't bother
|
// if the user wasn't created in the last minute, don't bother
|
||||||
const now = dayjs().utc()
|
const now = dayjs().utc()
|
||||||
const userCreatedTime = dayjs(user.createdTime)
|
const userCreatedTime = dayjs(user.createdTime)
|
||||||
if (now.diff(userCreatedTime, 'minute') > 1) return
|
if (now.diff(userCreatedTime, 'minute') > 5) return
|
||||||
|
|
||||||
const local = safeLocalStorage()
|
const local = safeLocalStorage()
|
||||||
const cachedReferralUsername = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
|
const cachedReferralUsername = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
|
||||||
const cachedReferralContractId = local?.getItem(
|
const cachedReferralContractId = local?.getItem(
|
||||||
CACHED_REFERRAL_CONTRACT_ID_KEY
|
CACHED_REFERRAL_CONTRACT_ID_KEY
|
||||||
)
|
)
|
||||||
const cachedReferralGroupSlug = local?.getItem(CACHED_REFERRAL_GROUP_SLUG_KEY)
|
const cachedReferralGroupId = local?.getItem(CACHED_REFERRAL_GROUP_ID_KEY)
|
||||||
|
|
||||||
// get user via username
|
// get user via username
|
||||||
if (cachedReferralUsername)
|
if (cachedReferralUsername)
|
||||||
|
@ -155,6 +155,9 @@ async function setCachedReferralInfoForUser(user: User | null) {
|
||||||
referredByContractId: cachedReferralContractId
|
referredByContractId: cachedReferralContractId
|
||||||
? cachedReferralContractId
|
? cachedReferralContractId
|
||||||
: undefined,
|
: undefined,
|
||||||
|
referredByGroupId: cachedReferralGroupId
|
||||||
|
? cachedReferralGroupId
|
||||||
|
: undefined,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -165,15 +168,14 @@ async function setCachedReferralInfoForUser(user: User | null) {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
referredByUserId: referredByUser.id,
|
referredByUserId: referredByUser.id,
|
||||||
referredByContractId: cachedReferralContractId,
|
referredByContractId: cachedReferralContractId,
|
||||||
referredByGroupSlug: cachedReferralGroupSlug,
|
referredByGroupId: cachedReferralGroupId,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (cachedReferralGroupSlug)
|
if (cachedReferralGroupId) addUserToGroupViaId(cachedReferralGroupId, user.id)
|
||||||
addUserToGroupViaSlug(cachedReferralGroupSlug, user.id)
|
|
||||||
|
|
||||||
local?.removeItem(CACHED_REFERRAL_GROUP_SLUG_KEY)
|
local?.removeItem(CACHED_REFERRAL_GROUP_ID_KEY)
|
||||||
local?.removeItem(CACHED_REFERRAL_USERNAME_KEY)
|
local?.removeItem(CACHED_REFERRAL_USERNAME_KEY)
|
||||||
local?.removeItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
|
local?.removeItem(CACHED_REFERRAL_CONTRACT_ID_KEY)
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ export default function GroupPage(props: {
|
||||||
referrer?: string
|
referrer?: string
|
||||||
}
|
}
|
||||||
if (!user && router.isReady)
|
if (!user && router.isReady)
|
||||||
writeReferralInfo(creator.username, undefined, referrer, group?.slug)
|
writeReferralInfo(creator.username, undefined, referrer, group?.id)
|
||||||
}, [user, creator, group, router])
|
}, [user, creator, group, router])
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
|
|
@ -713,8 +713,12 @@ function QuestionOrGroupLink(props: {
|
||||||
href={
|
href={
|
||||||
sourceContractCreatorUsername
|
sourceContractCreatorUsername
|
||||||
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
: (sourceType === 'group' || sourceType === 'tip') && sourceSlug
|
: // User's added to group or received a tip there
|
||||||
|
(sourceType === 'group' || sourceType === 'tip') && sourceSlug
|
||||||
? `${groupPath(sourceSlug)}`
|
? `${groupPath(sourceSlug)}`
|
||||||
|
: // User referral via group
|
||||||
|
sourceSlug?.includes('/group/')
|
||||||
|
? `${sourceSlug}`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
@ -745,12 +749,16 @@ function getSourceUrl(notification: Notification) {
|
||||||
} = notification
|
} = notification
|
||||||
if (sourceType === 'follow') return `/${sourceUserUsername}`
|
if (sourceType === 'follow') return `/${sourceUserUsername}`
|
||||||
if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
|
if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
|
||||||
|
// User referral via contract:
|
||||||
if (
|
if (
|
||||||
sourceContractCreatorUsername &&
|
sourceContractCreatorUsername &&
|
||||||
sourceContractSlug &&
|
sourceContractSlug &&
|
||||||
sourceType === 'user'
|
sourceType === 'user'
|
||||||
)
|
)
|
||||||
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
|
// User referral:
|
||||||
|
if (sourceType === 'user' && !sourceContractSlug)
|
||||||
|
return `/${sourceUserUsername}`
|
||||||
if (sourceType === 'tip' && sourceContractSlug)
|
if (sourceType === 'tip' && sourceContractSlug)
|
||||||
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
|
||||||
if (sourceType === 'tip' && sourceSlug) return `${groupPath(sourceSlug)}`
|
if (sourceType === 'tip' && sourceSlug) return `${groupPath(sourceSlug)}`
|
||||||
|
|
Loading…
Reference in New Issue
Block a user