From e712ad82891e2d14d685442ae7689c7d51fea4e6 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Mon, 4 Jul 2022 07:49:41 -0600 Subject: [PATCH] Allow users to choose who referred them (#611) * Allow users to choose who referred them * Refactor * Rewording * Match list styles * Match empty text styles --- firestore.rules | 19 ++- web/components/filter-select-users.tsx | 194 +++++++++++++++---------- web/components/referrals-button.tsx | 96 +++++++++++- web/components/user-page.tsx | 2 +- web/pages/account.tsx | 41 ------ web/pages/link/[slug].tsx | 2 +- 6 files changed, 221 insertions(+), 133 deletions(-) delete mode 100644 web/pages/account.tsx diff --git a/firestore.rules b/firestore.rules index 4645343d..28ff4485 100644 --- a/firestore.rules +++ b/firestore.rules @@ -21,16 +21,15 @@ service cloud.firestore { allow update: if resource.data.id == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'referredByContractId']); - allow update: if resource.data.id == request.auth.uid - && request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['referredByUserId']) - // only one referral allowed per user - && !("referredByUserId" in resource.data) - // user can't refer themselves - && (resource.data.id != request.resource.data.referredByUserId) - // user can't refer someone who referred them quid pro quo - && get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId != resource.data.id; - + allow update: if resource.data.id == request.auth.uid + && request.resource.data.diff(resource.data).affectedKeys() + .hasOnly(['referredByUserId']) + // only one referral allowed per user + && !("referredByUserId" in resource.data) + // user can't refer themselves + && !(resource.data.id == request.resource.data.referredByUserId); + // quid pro quos enabled (only once though so nbd) - bc I can't make this work: + // && (get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId == resource.data.id); } match /{somePath=**}/portfolioHistory/{portfolioHistoryId} { diff --git a/web/components/filter-select-users.tsx b/web/components/filter-select-users.tsx index 93badf20..8d2dbbae 100644 --- a/web/components/filter-select-users.tsx +++ b/web/components/filter-select-users.tsx @@ -1,4 +1,4 @@ -import { UserIcon } from '@heroicons/react/outline' +import { UserIcon, XIcon } from '@heroicons/react/outline' import { useUsers } from 'web/hooks/use-users' import { User } from 'common/user' import { Fragment, useMemo, useState } from 'react' @@ -6,13 +6,24 @@ import clsx from 'clsx' import { Menu, Transition } from '@headlessui/react' import { Avatar } from 'web/components/avatar' import { Row } from 'web/components/layout/row' +import { UserLink } from 'web/components/user-page' export function FilterSelectUsers(props: { setSelectedUsers: (users: User[]) => void selectedUsers: User[] ignoreUserIds: string[] + showSelectedUsersTitle?: boolean + selectedUsersClassName?: string + maxUsers?: number }) { - const { ignoreUserIds, selectedUsers, setSelectedUsers } = props + const { + ignoreUserIds, + selectedUsers, + setSelectedUsers, + showSelectedUsersTitle, + selectedUsersClassName, + maxUsers, + } = props const users = useUsers() const [query, setQuery] = useState('') const [filteredUsers, setFilteredUsers] = useState([]) @@ -29,89 +40,118 @@ export function FilterSelectUsers(props: { }) ) }, [beginQuerying, users, selectedUsers, ignoreUserIds, query]) - + const shouldShow = maxUsers ? selectedUsers.length < maxUsers : true return (
-
-
-
- setQuery(e.target.value)} - className="input input-bordered block w-full pl-10 focus:border-gray-300 " - placeholder="Austin Chen" - /> -
- - {({}) => ( - +
+
+
+ setQuery(e.target.value)} + className="input input-bordered block w-full pl-10 focus:border-gray-300 " + placeholder="Austin Chen" + /> +
+ - -
- {filteredUsers.map((user: User) => ( - - {({ active }) => ( - ( + + +
+ {filteredUsers.map((user: User) => ( + + {({ active }) => ( + { + setQuery('') + setSelectedUsers([...selectedUsers, user]) + }} + > + + {user.name} + )} - onClick={() => { - setQuery('') - setSelectedUsers([...selectedUsers, user]) - }} - > - - {user.name} - - )} - - ))} -
-
-
- )} -
+ + ))} +
+ + + )} + + + )} {selectedUsers.length > 0 && ( <> -
Added members:
- +
+ {showSelectedUsersTitle && 'Added members:'} +
+ {selectedUsers.map((user: User) => ( -
- + + + + + + setSelectedUsers([ + ...selectedUsers.filter((u) => u.id != user.id), + ]) + } + className=" h-5 w-5 cursor-pointer text-gray-400" + aria-hidden="true" /> - {user.name}
))}
diff --git a/web/components/referrals-button.tsx b/web/components/referrals-button.tsx index c23958fc..bb9e53cb 100644 --- a/web/components/referrals-button.tsx +++ b/web/components/referrals-button.tsx @@ -10,9 +10,11 @@ import { Row } from 'web/components/layout/row' import { Avatar } from 'web/components/avatar' import { UserLink } from 'web/components/user-page' import { useReferrals } from 'web/hooks/use-referrals' +import { FilterSelectUsers } from 'web/components/filter-select-users' +import { getUser, updateUser } from 'web/lib/firebase/users' -export function ReferralsButton(props: { user: User }) { - const { user } = props +export function ReferralsButton(props: { user: User; currentUser?: User }) { + const { user, currentUser } = props const [isOpen, setIsOpen] = useState(false) const referralIds = useReferrals(user.id) @@ -28,6 +30,7 @@ export function ReferralsButton(props: { user: User }) { referralIds={referralIds ?? []} isOpen={isOpen} setIsOpen={setIsOpen} + currentUser={currentUser} /> ) @@ -38,8 +41,26 @@ function ReferralsDialog(props: { referralIds: string[] isOpen: boolean setIsOpen: (isOpen: boolean) => void + currentUser?: User }) { - const { user, referralIds, isOpen, setIsOpen } = props + const { user, referralIds, isOpen, setIsOpen, currentUser } = props + const [referredBy, setReferredBy] = useState([]) + const [isSubmitting, setIsSubmitting] = useState(false) + const [errorText, setErrorText] = useState('') + + const [referredByUser, setReferredByUser] = useState() + useEffect(() => { + if ( + isOpen && + !referredByUser && + currentUser?.referredByUserId && + currentUser.id === user.id + ) { + getUser(currentUser.referredByUserId).then((user) => { + setReferredByUser(user) + }) + } + }, [currentUser, isOpen, referredByUser, user.id]) useEffect(() => { prefetchUsers(referralIds) @@ -56,6 +77,75 @@ function ReferralsDialog(props: { title: 'Referrals', content: , }, + { + title: 'Referred by', + content: ( + <> + {user.id === currentUser?.id && !referredByUser ? ( + <> + + + + + + {referredBy.length > 0 && + 'Careful: you can only set who referred you once!'} + + {errorText} + + ) : ( +
+ {referredByUser ? ( + + + + + ) : ( + No one... + )} +
+ )} + + ), + }, ]} /> diff --git a/web/components/user-page.tsx b/web/components/user-page.tsx index d72a2a16..0a1366c4 100644 --- a/web/components/user-page.tsx +++ b/web/components/user-page.tsx @@ -202,7 +202,7 @@ export function UserPage(props: { - + diff --git a/web/pages/account.tsx b/web/pages/account.tsx deleted file mode 100644 index 59d938c3..00000000 --- a/web/pages/account.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react' -import { Page } from 'web/components/page' -import { UserPage } from 'web/components/user-page' -import { useUser } from 'web/hooks/use-user' -import { firebaseLogin } from 'web/lib/firebase/users' - -function SignInCard() { - return ( -
-
- -
-
-

Welcome!

-

Sign in to get started

-
- -
-
-
- ) -} - -export default function Account() { - const user = useUser() - return user ? ( - - ) : ( - - - - ) -} diff --git a/web/pages/link/[slug].tsx b/web/pages/link/[slug].tsx index 60966756..eed68e1a 100644 --- a/web/pages/link/[slug].tsx +++ b/web/pages/link/[slug].tsx @@ -46,7 +46,7 @@ export default function ClaimPage() { if (result.data.status == 'error') { throw new Error(result.data.message) } - router.push('/account?claimed-mana=yes') + user && router.push(`/${user.username}?claimed-mana=yes`) } catch (e) { console.log(e) const message =