Referrals bug fix and attribute group

This commit is contained in:
Ian Philips 2022-07-18 10:40:44 -06:00
parent db537a97ba
commit 39c38a669e
9 changed files with 94 additions and 42 deletions

View File

@ -63,3 +63,4 @@ export type notification_reason_types =
| 'on_group_you_are_member_of'
| 'tip_received'
| 'bet_fill'
| 'user_joined_from_your_group_invite'

View File

@ -38,6 +38,7 @@ export type User = {
referredByUserId?: string
referredByContractId?: string
referredByGroupId?: string
lastPingTime?: number
}

View File

@ -22,11 +22,11 @@ service cloud.firestore {
allow read;
allow update: if resource.data.id == request.auth.uid
&& 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
allow update: if resource.data.id == request.auth.uid
&& request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['referredByUserId'])
.hasOnly(['referredByUserId', 'referredByContractId', 'referredByGroupId'])
// only one referral allowed per user
&& !("referredByUserId" in resource.data)
// user can't refer themselves

View File

@ -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 (
userToReasonTexts: user_to_reason_texts,
userId: string
@ -284,8 +270,6 @@ export const createNotification = async (
} else if (sourceType === 'group' && relatedUserId) {
if (sourceUpdateType === 'created')
await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
} else if (sourceType === 'user' && relatedUserId) {
await notifyUserReceivedReferralBonus(userToReasonTexts, relatedUserId)
}
// The following functions need sourceContract to be defined.
@ -435,3 +419,52 @@ export const createGroupCommentNotification = async (
}
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}`

View File

@ -2,11 +2,12 @@ import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { REFERRAL_AMOUNT, User } from '../../common/user'
import { HOUSE_LIQUIDITY_PROVIDER_ID } from '../../common/antes'
import { createNotification } from './create-notification'
import { createReferralNotification } from './create-notification'
import { ReferralTxn } from '../../common/txn'
import { Contract } from '../../common/contract'
import { LimitBet } from 'common/bet'
import { QuerySnapshot } from 'firebase-admin/firestore'
import { Group } from 'common/group'
const firestore = admin.firestore()
export const onUpdateUser = functions.firestore
@ -54,6 +55,17 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
}
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 = (
await firestore
.collection('txns')
@ -100,18 +112,13 @@ async function handleUserUpdatedReferral(user: User, eventId: string) {
totalDeposits: referredByUser.totalDeposits + REFERRAL_AMOUNT,
})
await createNotification(
user.id,
'user',
'updated',
await createReferralNotification(
referredByUser,
user,
eventId,
txn.amount.toString(),
referredByContract,
'user',
referredByUser.id,
referredByContract?.slug,
referredByContract?.question
referredByGroup
)
})
}

View File

@ -89,11 +89,11 @@ export async function getGroupsWithContractId(
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
const group = await getGroupBySlug(groupSlug)
const group = await getGroup(groupId)
if (!group) {
console.error(`Group not found: ${groupSlug}`)
console.error(`Group not found: ${groupId}`)
return
}
return await joinGroup(group, userId)

View File

@ -35,7 +35,7 @@ import { feed } from 'common/feed'
import { CATEGORY_LIST } from 'common/categories'
import { safeLocalStorage } from '../util/local'
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 { randomString } from 'common/util/random'
import dayjs from 'dayjs'
@ -99,13 +99,13 @@ export function listenForPrivateUser(
const CACHED_USER_KEY = 'CACHED_USER_KEY'
const CACHED_REFERRAL_USERNAME_KEY = 'CACHED_REFERRAL_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(
defaultReferrerUsername: string,
contractId?: string,
referralUsername?: string,
groupSlug?: string
groupId?: string
) {
const local = safeLocalStorage()
const cachedReferralUser = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
@ -121,7 +121,7 @@ export function writeReferralInfo(
local?.setItem(CACHED_REFERRAL_USERNAME_KEY, referralUsername)
// 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.
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
const now = dayjs().utc()
const userCreatedTime = dayjs(user.createdTime)
if (now.diff(userCreatedTime, 'minute') > 1) return
if (now.diff(userCreatedTime, 'minute') > 5) return
const local = safeLocalStorage()
const cachedReferralUsername = local?.getItem(CACHED_REFERRAL_USERNAME_KEY)
const cachedReferralContractId = local?.getItem(
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
if (cachedReferralUsername)
@ -155,6 +155,9 @@ async function setCachedReferralInfoForUser(user: User | null) {
referredByContractId: cachedReferralContractId
? cachedReferralContractId
: undefined,
referredByGroupId: cachedReferralGroupId
? cachedReferralGroupId
: undefined,
})
)
.catch((err) => {
@ -165,15 +168,14 @@ async function setCachedReferralInfoForUser(user: User | null) {
userId: user.id,
referredByUserId: referredByUser.id,
referredByContractId: cachedReferralContractId,
referredByGroupSlug: cachedReferralGroupSlug,
referredByGroupId: cachedReferralGroupId,
})
})
})
if (cachedReferralGroupSlug)
addUserToGroupViaSlug(cachedReferralGroupSlug, user.id)
if (cachedReferralGroupId) addUserToGroupViaId(cachedReferralGroupId, 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_CONTRACT_ID_KEY)
}

View File

@ -161,7 +161,7 @@ export default function GroupPage(props: {
referrer?: string
}
if (!user && router.isReady)
writeReferralInfo(creator.username, undefined, referrer, group?.slug)
writeReferralInfo(creator.username, undefined, referrer, group?.id)
}, [user, creator, group, router])
const { width } = useWindowSize()

View File

@ -713,8 +713,12 @@ function QuestionOrGroupLink(props: {
href={
sourceContractCreatorUsername
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
: (sourceType === 'group' || sourceType === 'tip') && sourceSlug
: // User's added to group or received a tip there
(sourceType === 'group' || sourceType === 'tip') && sourceSlug
? `${groupPath(sourceSlug)}`
: // User referral via group
sourceSlug?.includes('/group/')
? `${sourceSlug}`
: ''
}
onClick={() =>
@ -745,12 +749,16 @@ function getSourceUrl(notification: Notification) {
} = notification
if (sourceType === 'follow') return `/${sourceUserUsername}`
if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
// User referral via contract:
if (
sourceContractCreatorUsername &&
sourceContractSlug &&
sourceType === 'user'
)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
// User referral:
if (sourceType === 'user' && !sourceContractSlug)
return `/${sourceUserUsername}`
if (sourceType === 'tip' && sourceContractSlug)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
if (sourceType === 'tip' && sourceSlug) return `${groupPath(sourceSlug)}`