Inga/manalink bug fixes (#653)

* fixed manalinks bug of claiming own manalink, and also rerouting to home upon claiming if not logged in
* no more multiple hardcoded manalink messages
This commit is contained in:
ingawei 2022-07-15 18:42:37 -07:00 committed by GitHub
parent eed7990c3c
commit 7d24a3e4a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 34 additions and 25 deletions

View File

@ -27,6 +27,7 @@ Adapted from https://firebase.google.com/docs/functions/get-started
1. `$ brew install java` 1. `$ brew install java`
2. `$ sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk` 2. `$ sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk`
2. `$ gcloud auth login` to authenticate the CLI tools to Google Cloud 2. `$ gcloud auth login` to authenticate the CLI tools to Google Cloud
3. `$ gcloud config set project <project-id>` to choose the project (`$ gcloud projects list` to see options) 3. `$ gcloud config set project <project-id>` to choose the project (`$ gcloud projects list` to see options)
4. `$ mkdir firestore_export` to create a folder to store the exported database 4. `$ mkdir firestore_export` to create a folder to store the exported database
@ -53,7 +54,10 @@ Adapted from https://firebase.google.com/docs/functions/get-started
## Deploying ## Deploying
0. `$ firebase use prod` to switch to prod 0. After merging, you need to manually deploy to backend:
1. `git checkout main`
1. `git pull origin main`
1. `$ firebase use prod` to switch to prod
1. `$ firebase deploy --only functions` to push your changes live! 1. `$ firebase deploy --only functions` to push your changes live!
(Future TODO: auto-deploy functions on Git push) (Future TODO: auto-deploy functions on Git push)

View File

@ -28,6 +28,9 @@ export const claimmanalink = newEndpoint({}, async (req, auth) => {
if (amount <= 0 || isNaN(amount) || !isFinite(amount)) if (amount <= 0 || isNaN(amount) || !isFinite(amount))
throw new APIError(500, 'Invalid amount') throw new APIError(500, 'Invalid amount')
if (auth.uid === fromId)
throw new APIError(400, `You can't claim your own manalink`)
const fromDoc = firestore.doc(`users/${fromId}`) const fromDoc = firestore.doc(`users/${fromId}`)
const fromSnap = await transaction.get(fromDoc) const fromSnap = await transaction.get(fromDoc)
if (!fromSnap.exists) { if (!fromSnap.exists) {

View File

@ -2,12 +2,13 @@ import { ReactNode } from 'react'
import clsx from 'clsx' import clsx from 'clsx'
export function Button(props: { export function Button(props: {
children: ReactNode
className?: string className?: string
onClick?: () => void onClick?: () => void
children?: ReactNode
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
color?: 'green' | 'red' | 'blue' | 'indigo' | 'yellow' | 'gray' color?: 'green' | 'red' | 'blue' | 'indigo' | 'yellow' | 'gray'
type?: 'button' | 'reset' | 'submit' type?: 'button' | 'reset' | 'submit'
disabled?: boolean
}) { }) {
const { const {
children, children,
@ -16,6 +17,7 @@ export function Button(props: {
size = 'md', size = 'md',
color = 'indigo', color = 'indigo',
type = 'button', type = 'button',
disabled = false,
} = props } = props
const sizeClasses = { const sizeClasses = {
@ -30,7 +32,7 @@ export function Button(props: {
<button <button
type={type} type={type}
className={clsx( className={clsx(
'font-md items-center justify-center rounded-md border border-transparent shadow-sm hover:transition-colors', 'font-md items-center justify-center rounded-md border border-transparent shadow-sm hover:transition-colors disabled:cursor-not-allowed disabled:opacity-50',
sizeClasses, sizeClasses,
color === 'green' && 'btn-primary text-white', color === 'green' && 'btn-primary text-white',
color === 'red' && 'bg-red-400 text-white hover:bg-red-500', color === 'red' && 'bg-red-400 text-white hover:bg-red-500',
@ -40,6 +42,7 @@ export function Button(props: {
color === 'gray' && 'bg-gray-200 text-gray-700 hover:bg-gray-300', color === 'gray' && 'bg-gray-200 text-gray-700 hover:bg-gray-300',
className className
)} )}
disabled={disabled}
onClick={onClick} onClick={onClick}
> >
{children} {children}

View File

@ -3,6 +3,8 @@ import { formatMoney } from 'common/util/format'
import { fromNow } from 'web/lib/util/time' import { fromNow } from 'web/lib/util/time'
import { Col } from 'web/components/layout/col' import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row' import { Row } from 'web/components/layout/row'
import { User } from 'web/lib/firebase/users'
import { Button } from './button'
export type ManalinkInfo = { export type ManalinkInfo = {
expiresTime: number | null expiresTime: number | null
@ -13,13 +15,13 @@ export type ManalinkInfo = {
} }
export function ManalinkCard(props: { export function ManalinkCard(props: {
user: User | null | undefined
className?: string className?: string
info: ManalinkInfo info: ManalinkInfo
defaultMessage: string
isClaiming: boolean isClaiming: boolean
onClaim?: () => void onClaim?: () => void
}) { }) {
const { className, defaultMessage, isClaiming, info, onClaim } = props const { user, className, isClaiming, info, onClaim } = props
const { expiresTime, maxUses, uses, amount, message } = info const { expiresTime, maxUses, uses, amount, message } = info
return ( return (
<div <div
@ -52,16 +54,13 @@ export function ManalinkCard(props: {
<div className="mb-1 text-xl text-indigo-500"> <div className="mb-1 text-xl text-indigo-500">
{formatMoney(amount)} {formatMoney(amount)}
</div> </div>
<div>{message || defaultMessage}</div> <div>{message}</div>
</Col> </Col>
<div className="ml-auto"> <div className="ml-auto">
<button <Button onClick={onClaim} disabled={isClaiming}>
className={clsx('btn', isClaiming ? 'loading disabled' : '')} {user ? 'Claim' : 'Login'}
onClick={onClaim} </Button>
>
{isClaiming ? '' : 'Claim'}
</button>
</div> </div>
</Row> </Row>
</div> </div>
@ -71,9 +70,8 @@ export function ManalinkCard(props: {
export function ManalinkCardPreview(props: { export function ManalinkCardPreview(props: {
className?: string className?: string
info: ManalinkInfo info: ManalinkInfo
defaultMessage: string
}) { }) {
const { className, defaultMessage, info } = props const { className, info } = props
const { expiresTime, maxUses, uses, amount, message } = info const { expiresTime, maxUses, uses, amount, message } = info
return ( return (
<div <div
@ -102,7 +100,7 @@ export function ManalinkCardPreview(props: {
<Row className="rounded-b-lg bg-white p-2"> <Row className="rounded-b-lg bg-white p-2">
<Col className="text-md"> <Col className="text-md">
<div className="mb-1 text-indigo-500">{formatMoney(amount)}</div> <div className="mb-1 text-indigo-500">{formatMoney(amount)}</div>
<div className="text-xs">{message || defaultMessage}</div> <div className="text-xs">{message}</div>
</Col> </Col>
</Row> </Row>
</div> </div>

View File

@ -66,12 +66,14 @@ function CreateManalinkForm(props: {
const defaultExpire = 'week' const defaultExpire = 'week'
const [expiresIn, setExpiresIn] = useState(defaultExpire) const [expiresIn, setExpiresIn] = useState(defaultExpire)
const defaultMessage = 'from ' + user.name
const [newManalink, setNewManalink] = useState<ManalinkInfo>({ const [newManalink, setNewManalink] = useState<ManalinkInfo>({
expiresTime: dayjs().add(1, defaultExpire).valueOf(), expiresTime: dayjs().add(1, defaultExpire).valueOf(),
amount: 100, amount: 100,
maxUses: 1, maxUses: 1,
uses: 0, uses: 0,
message: '', message: defaultMessage,
}) })
const EXPIRE_OPTIONS = { const EXPIRE_OPTIONS = {
@ -161,7 +163,7 @@ function CreateManalinkForm(props: {
<div className="form-control w-full"> <div className="form-control w-full">
<label className="label">Message</label> <label className="label">Message</label>
<Textarea <Textarea
placeholder={`From ${user.name}`} placeholder={defaultMessage}
className="input input-bordered resize-none" className="input input-bordered resize-none"
autoFocus autoFocus
value={newManalink.message} value={newManalink.message}
@ -189,11 +191,7 @@ function CreateManalinkForm(props: {
{finishedCreating && ( {finishedCreating && (
<> <>
<Title className="!my-0" text="Manalink Created!" /> <Title className="!my-0" text="Manalink Created!" />
<ManalinkCardPreview <ManalinkCardPreview className="my-4" info={newManalink} />
className="my-4"
defaultMessage={`From ${user.name}`}
info={newManalink}
/>
<Row <Row
className={clsx( className={clsx(
'rounded border bg-gray-50 py-2 px-3 text-sm text-gray-500 transition-colors duration-700', 'rounded border bg-gray-50 py-2 px-3 text-sm text-gray-500 transition-colors duration-700',

View File

@ -6,7 +6,6 @@ import { claimManalink } from 'web/lib/firebase/api'
import { useManalink } from 'web/lib/firebase/manalinks' import { useManalink } from 'web/lib/firebase/manalinks'
import { ManalinkCard } from 'web/components/manalink-card' import { ManalinkCard } from 'web/components/manalink-card'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { useUserById } from 'web/hooks/use-user'
import { firebaseLogin } from 'web/lib/firebase/users' import { firebaseLogin } from 'web/lib/firebase/users'
export default function ClaimPage() { export default function ClaimPage() {
@ -17,7 +16,6 @@ export default function ClaimPage() {
const [claiming, setClaiming] = useState(false) const [claiming, setClaiming] = useState(false)
const [error, setError] = useState<string | undefined>(undefined) const [error, setError] = useState<string | undefined>(undefined)
const fromUser = useUserById(manalink?.fromId)
if (!manalink) { if (!manalink) {
return <></> return <></>
} }
@ -33,7 +31,7 @@ export default function ClaimPage() {
<div className="mx-auto max-w-xl"> <div className="mx-auto max-w-xl">
<Title text={`Claim M$${manalink.amount} mana`} /> <Title text={`Claim M$${manalink.amount} mana`} />
<ManalinkCard <ManalinkCard
defaultMessage={fromUser?.name || 'Enjoy this mana!'} user={user}
info={info} info={info}
isClaiming={claiming} isClaiming={claiming}
onClaim={async () => { onClaim={async () => {
@ -41,6 +39,11 @@ export default function ClaimPage() {
try { try {
if (user == null) { if (user == null) {
await firebaseLogin() await firebaseLogin()
setClaiming(false)
return
}
if (user?.id == manalink.fromId) {
throw new Error("You can't claim your own manalink.")
} }
await claimManalink({ slug: manalink.slug }) await claimManalink({ slug: manalink.slug })
user && router.push(`/${user.username}?claimed-mana=yes`) user && router.push(`/${user.username}?claimed-mana=yes`)