Refactor some backend-related stuff (#639)
* web/lib/firebase/api-call -> common/api, web/lib/firebase/api * Reuse `APIError` type in server code * Reuse `getFunctionUrl` in server code
This commit is contained in:
parent
6462d4a2ed
commit
4700ceb14c
22
common/api.ts
Normal file
22
common/api.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { ENV_CONFIG } from 'common/envs/constants'
|
||||
|
||||
export class APIError extends Error {
|
||||
code: number
|
||||
details?: unknown
|
||||
constructor(code: number, message: string, details?: unknown) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'APIError'
|
||||
this.details = details
|
||||
}
|
||||
}
|
||||
|
||||
export function getFunctionUrl(name: string) {
|
||||
if (process.env.NEXT_PUBLIC_FIREBASE_EMULATE) {
|
||||
const { projectId, region } = ENV_CONFIG.firebaseConfig
|
||||
return `http://localhost:5001/${projectId}/${region}/${name}`
|
||||
} else {
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
return `https://${name}-${cloudRunId}-${cloudRunRegion}.a.run.app`
|
||||
}
|
||||
}
|
|
@ -3,13 +3,14 @@ import { logger } from 'firebase-functions/v2'
|
|||
import { HttpsOptions, onRequest, Request } from 'firebase-functions/v2/https'
|
||||
import { log } from './utils'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { APIError } from '../../common/api'
|
||||
import { PrivateUser } from '../../common/user'
|
||||
import {
|
||||
CORS_ORIGIN_MANIFOLD,
|
||||
CORS_ORIGIN_LOCALHOST,
|
||||
CORS_ORIGIN_VERCEL,
|
||||
} from '../../common/envs/constants'
|
||||
export { APIError } from '../../common/api'
|
||||
|
||||
type Output = Record<string, unknown>
|
||||
type AuthedUser = {
|
||||
|
@ -21,17 +22,6 @@ type JwtCredentials = { kind: 'jwt'; data: admin.auth.DecodedIdToken }
|
|||
type KeyCredentials = { kind: 'key'; data: string }
|
||||
type Credentials = JwtCredentials | KeyCredentials
|
||||
|
||||
export class APIError {
|
||||
code: number
|
||||
msg: string
|
||||
details: unknown
|
||||
constructor(code: number, msg: string, details?: unknown) {
|
||||
this.code = code
|
||||
this.msg = msg
|
||||
this.details = details
|
||||
}
|
||||
}
|
||||
|
||||
const auth = admin.auth()
|
||||
const firestore = admin.firestore()
|
||||
const privateUsers = firestore.collection(
|
||||
|
@ -136,7 +126,7 @@ export const newEndpoint = (endpointOpts: EndpointOptions, fn: Handler) => {
|
|||
res.status(200).json(await fn(req, authedUser))
|
||||
} catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
const output: { [k: string]: unknown } = { message: e.msg }
|
||||
const output: { [k: string]: unknown } = { message: e.message }
|
||||
if (e.details != null) {
|
||||
output.details = e.details
|
||||
}
|
||||
|
|
|
@ -613,7 +613,7 @@
|
|||
>our Discord</a
|
||||
>! Or,
|
||||
<a
|
||||
href="https://unsubscribe-nggbo3neva-uc.a.run.app?id={{userId}}&type=market-resolve"
|
||||
href="{{unsubscribeUrl}}"
|
||||
style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
|
|
|
@ -635,7 +635,7 @@
|
|||
>our Discord</a
|
||||
>! Or,
|
||||
<a
|
||||
href="https://unsubscribe-nggbo3neva-uc.a.run.app?id={{userId}}&type=market-resolved"
|
||||
href="{{unsubscribeUrl}}"
|
||||
style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DOMAIN, ENV_CONFIG } from '../../common/envs/constants'
|
||||
import { DOMAIN } from '../../common/envs/constants'
|
||||
import { Answer } from '../../common/answer'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { getProbability } from '../../common/calculate'
|
||||
|
@ -16,6 +16,9 @@ import { formatNumericProbability } from '../../common/pseudo-numeric'
|
|||
|
||||
import { sendTemplateEmail } from './send-email'
|
||||
import { getPrivateUser, getUser } from './utils'
|
||||
import { getFunctionUrl } from '../../common/api'
|
||||
|
||||
const UNSUBSCRIBE_ENDPOINT = getFunctionUrl('unsubscribe')
|
||||
|
||||
export const sendMarketResolutionEmail = async (
|
||||
userId: string,
|
||||
|
@ -53,6 +56,9 @@ export const sendMarketResolutionEmail = async (
|
|||
? ` (plus ${formatMoney(creatorPayout)} in commissions)`
|
||||
: ''
|
||||
|
||||
const emailType = 'market-resolved'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const templateData: market_resolved_template = {
|
||||
userId: user.id,
|
||||
name: user.name,
|
||||
|
@ -62,6 +68,7 @@ export const sendMarketResolutionEmail = async (
|
|||
investment: `${Math.floor(investment)}`,
|
||||
payout: `${Math.floor(payout)}${creatorPayoutText}`,
|
||||
url: `https://${DOMAIN}/${creator.username}/${contract.slug}`,
|
||||
unsubscribeUrl,
|
||||
}
|
||||
|
||||
// Modify template here:
|
||||
|
@ -85,6 +92,7 @@ type market_resolved_template = {
|
|||
investment: string
|
||||
payout: string
|
||||
url: string
|
||||
unsubscribeUrl: string
|
||||
}
|
||||
|
||||
const toDisplayResolution = (
|
||||
|
@ -141,8 +149,7 @@ export const sendWelcomeEmail = async (
|
|||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
const unsubscribeLink = `https://unsubscribe-${cloudRunId}-${cloudRunRegion}.a.run.app?id=${userId}&type=${emailType}`
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -174,8 +181,7 @@ export const sendOneWeekBonusEmail = async (
|
|||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
const unsubscribeLink = `https://unsubscribe-${cloudRunId}-${cloudRunRegion}.a.run.app?id=${userId}&type=${emailType}`
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -207,8 +213,7 @@ export const sendThankYouEmail = async (
|
|||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
const unsubscribeLink = `https://unsubscribe-${cloudRunId}-${cloudRunRegion}.a.run.app?id=${userId}&type=${emailType}`
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -242,6 +247,8 @@ export const sendMarketCloseEmail = async (
|
|||
const { question, slug, volume, mechanism, collectedFees } = contract
|
||||
|
||||
const url = `https://${DOMAIN}/${username}/${slug}`
|
||||
const emailType = 'market-resolve'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -250,6 +257,7 @@ export const sendMarketCloseEmail = async (
|
|||
{
|
||||
question,
|
||||
url,
|
||||
unsubscribeUrl,
|
||||
userId,
|
||||
name: firstName,
|
||||
volume: formatMoney(volume),
|
||||
|
@ -281,8 +289,7 @@ export const sendNewCommentEmail = async (
|
|||
const { question, creatorUsername, slug } = contract
|
||||
const marketUrl = `https://${DOMAIN}/${creatorUsername}/${slug}#${comment.id}`
|
||||
const emailType = 'market-comment'
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
const unsubscribeUrl = `https://unsubscribe-${cloudRunId}-${cloudRunRegion}.a.run.app?id=${userId}&type=${emailType}`
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const { name: commentorName, avatarUrl: commentorAvatarUrl } = commentCreator
|
||||
const { text } = comment
|
||||
|
@ -364,8 +371,7 @@ export const sendNewAnswerEmail = async (
|
|||
|
||||
const marketUrl = `https://${DOMAIN}/${creatorUsername}/${slug}`
|
||||
const emailType = 'market-answer'
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
const unsubscribeUrl = `https://unsubscribe-${cloudRunId}-${cloudRunRegion}.a.run.app?id=${userId}&type=${emailType}`
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const subject = `New answer on ${question}`
|
||||
const from = `${name} <info@manifold.markets>`
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Answer } from 'common/answer'
|
|||
import { FreeResponseContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api'
|
||||
import { Row } from '../layout/row'
|
||||
import { Spacer } from '../layout/spacer'
|
||||
import {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useState } from 'react'
|
|||
|
||||
import { Contract, FreeResponse } from 'common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api'
|
||||
import { Row } from '../layout/row'
|
||||
import { ChooseCancelSelector } from '../yes-no-selector'
|
||||
import { ResolveConfirmationButton } from '../confirmation-button'
|
||||
|
|
|
@ -6,7 +6,7 @@ import { findBestMatch } from 'string-similarity'
|
|||
import { FreeResponseContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { APIError, createAnswer } from 'web/lib/firebase/api-call'
|
||||
import { APIError, createAnswer } from 'web/lib/firebase/api'
|
||||
import { Row } from '../layout/row'
|
||||
import {
|
||||
formatMoney,
|
||||
|
|
|
@ -19,8 +19,8 @@ import { getBinaryCpmmBetInfo } from 'common/new-bet'
|
|||
import { Title } from './title'
|
||||
import { User } from 'web/lib/firebase/users'
|
||||
import { Bet, LimitBet } from 'common/bet'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
import { sellShares } from 'web/lib/firebase/api-call'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api'
|
||||
import { sellShares } from 'web/lib/firebase/api'
|
||||
import { AmountInput, BuyAmountInput } from './amount-input'
|
||||
import { InfoTooltip } from './info-tooltip'
|
||||
import { BinaryOutcomeLabel } from './outcome-label'
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from 'web/lib/firebase/contracts'
|
||||
import { Row } from './layout/row'
|
||||
import { UserLink } from './user-page'
|
||||
import { sellBet } from 'web/lib/firebase/api-call'
|
||||
import { sellBet } from 'web/lib/firebase/api'
|
||||
import { ConfirmationButton } from './confirmation-button'
|
||||
import { OutcomeLabel, YesLabel, NoLabel } from './outcome-label'
|
||||
import { filterDefined } from 'common/util/array'
|
||||
|
|
|
@ -22,14 +22,14 @@ import {
|
|||
import { useState } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { useUserContractBets } from 'web/hooks/use-user-bets'
|
||||
import { placeBet } from 'web/lib/firebase/api-call'
|
||||
import { placeBet } from 'web/lib/firebase/api'
|
||||
import { getBinaryProb, getBinaryProbPercent } from 'web/lib/firebase/contracts'
|
||||
import TriangleDownFillIcon from 'web/lib/icons/triangle-down-fill-icon'
|
||||
import TriangleFillIcon from 'web/lib/icons/triangle-fill-icon'
|
||||
import { Col } from '../layout/col'
|
||||
import { OUTCOME_TO_COLOR } from '../outcome-label'
|
||||
import { useSaveBinaryShares } from '../use-save-binary-shares'
|
||||
import { sellShares } from 'web/lib/firebase/api-call'
|
||||
import { sellShares } from 'web/lib/firebase/api'
|
||||
import { calculateCpmmSale, getCpmmProbability } from 'common/calculate-cpmm'
|
||||
import { track } from 'web/lib/service/analytics'
|
||||
import { formatNumericProbability } from 'common/pseudo-numeric'
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Title } from '../title'
|
|||
import { FilterSelectUsers } from 'web/components/filter-select-users'
|
||||
import { User } from 'common/user'
|
||||
import { MAX_GROUP_NAME_LENGTH } from 'common/group'
|
||||
import { createGroup } from 'web/lib/firebase/api-call'
|
||||
import { createGroup } from 'web/lib/firebase/api'
|
||||
|
||||
export function CreateGroupButton(props: {
|
||||
user: User
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getFormattedMappedValue } from 'common/pseudo-numeric'
|
|||
import { formatMoney, formatPercent } from 'common/util/format'
|
||||
import { sortBy } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
import { cancelBet } from 'web/lib/firebase/api-call'
|
||||
import { cancelBet } from 'web/lib/firebase/api'
|
||||
import { Col } from './layout/col'
|
||||
import { LoadingIndicator } from './loading-indicator'
|
||||
import { BinaryOutcomeLabel, PseudoNumericOutcomeLabel } from './outcome-label'
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'
|
|||
import { CPMMContract } from 'common/contract'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { addLiquidity, withdrawLiquidity } from 'web/lib/firebase/api-call'
|
||||
import { addLiquidity, withdrawLiquidity } from 'web/lib/firebase/api'
|
||||
import { AmountInput } from './amount-input'
|
||||
import { Row } from './layout/row'
|
||||
import { useUserLiquidity } from 'web/hooks/use-liquidity'
|
||||
|
|
|
@ -6,7 +6,7 @@ import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
|||
import { useRouter } from 'next/router'
|
||||
import { useUnseenPreferredNotificationGroups } from 'web/hooks/use-notifications'
|
||||
import { NOTIFICATIONS_PER_PAGE } from 'web/pages/notifications'
|
||||
import { requestBonuses } from 'web/lib/firebase/api-call'
|
||||
import { requestBonuses } from 'web/lib/firebase/api'
|
||||
import { PrivateUser } from 'common/user'
|
||||
|
||||
export default function NotificationsIcon(props: { className?: string }) {
|
||||
|
|
|
@ -10,9 +10,9 @@ import {
|
|||
import { NumericContract } from 'common/contract'
|
||||
import { formatPercent, formatMoney } from 'common/util/format'
|
||||
|
||||
import { useUser } from '../hooks/use-user'
|
||||
import { APIError, placeBet } from '../lib/firebase/api-call'
|
||||
import { User } from '../lib/firebase/users'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api'
|
||||
import { User } from 'web/lib/firebase/users'
|
||||
import { BuyAmountInput } from './amount-input'
|
||||
import { BucketInput } from './bucket-input'
|
||||
import { Col } from './layout/col'
|
||||
|
|
|
@ -7,7 +7,7 @@ import { NumberCancelSelector } from './yes-no-selector'
|
|||
import { Spacer } from './layout/spacer'
|
||||
import { ResolveConfirmationButton } from './confirmation-button'
|
||||
import { NumericContract, PseudoNumericContract } from 'common/contract'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api'
|
||||
import { BucketInput } from './bucket-input'
|
||||
import { getPseudoProbability } from 'common/pseudo-numeric'
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { User } from 'web/lib/firebase/users'
|
|||
import { YesNoCancelSelector } from './yes-no-selector'
|
||||
import { Spacer } from './layout/spacer'
|
||||
import { ResolveConfirmationButton } from './confirmation-button'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { APIError, resolveMarket } from 'web/lib/firebase/api'
|
||||
import { ProbabilitySelector } from './probability-selector'
|
||||
import { DPM_CREATOR_FEE } from 'common/fees'
|
||||
import { getProbability } from 'common/calculate'
|
||||
|
|
|
@ -11,7 +11,7 @@ import { debounce, sum } from 'lodash'
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import { CommentTips } from 'web/hooks/use-tip-txns'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { transact } from 'web/lib/firebase/api-call'
|
||||
import { transact } from 'web/lib/firebase/api'
|
||||
import { track } from 'web/lib/service/analytics'
|
||||
import { Row } from './layout/row'
|
||||
import { Tooltip } from './tooltip'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { promisify } from 'util'
|
||||
import { pipeline } from 'stream'
|
||||
import { getFunctionUrl } from 'web/lib/firebase/api-call'
|
||||
import { getFunctionUrl } from 'common/api'
|
||||
import fetch, { Headers, Response } from 'node-fetch'
|
||||
|
||||
function getProxiedRequestHeaders(req: NextApiRequest, whitelist: string[]) {
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
import { auth } from './users'
|
||||
import { ENV_CONFIG } from 'common/envs/constants'
|
||||
|
||||
export class APIError extends Error {
|
||||
code: number
|
||||
details?: string
|
||||
constructor(code: number, message: string, details?: string) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'APIError'
|
||||
this.details = details
|
||||
}
|
||||
}
|
||||
import { APIError, getFunctionUrl } from 'common/api'
|
||||
export { APIError } from 'common/api'
|
||||
|
||||
export async function call(url: string, method: string, params: any) {
|
||||
const user = auth.currentUser
|
||||
|
@ -35,21 +25,6 @@ export async function call(url: string, method: string, params: any) {
|
|||
})
|
||||
}
|
||||
|
||||
// Our users access the API through the Vercel proxy routes at /api/v0/blah,
|
||||
// but right now at least until we get performance under control let's have the
|
||||
// app just hit the cloud functions directly -- there's no difference and it's
|
||||
// one less hop
|
||||
|
||||
export function getFunctionUrl(name: string) {
|
||||
if (process.env.NEXT_PUBLIC_FIREBASE_EMULATE) {
|
||||
const { projectId, region } = ENV_CONFIG.firebaseConfig
|
||||
return `http://localhost:5001/${projectId}/${region}/${name}`
|
||||
} else {
|
||||
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
|
||||
return `https://${name}-${cloudRunId}-${cloudRunRegion}.a.run.app`
|
||||
}
|
||||
}
|
||||
|
||||
export function createAnswer(params: any) {
|
||||
return call(getFunctionUrl('createanswer'), 'POST', params)
|
||||
}
|
|
@ -23,7 +23,7 @@ import {
|
|||
import { zip } from 'lodash'
|
||||
import { app, db } from './init'
|
||||
import { PortfolioMetrics, PrivateUser, User } from 'common/user'
|
||||
import { createUser } from './api-call'
|
||||
import { createUser } from './api'
|
||||
import {
|
||||
coll,
|
||||
getValue,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { getFunctionUrl } from 'web/lib/firebase/api-call'
|
||||
import { getFunctionUrl } from 'common/api'
|
||||
|
||||
export const checkoutURL = (
|
||||
userId: string,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { Spacer } from 'web/components/layout/spacer'
|
|||
import { User } from 'common/user'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { Linkify } from 'web/components/linkify'
|
||||
import { transact } from 'web/lib/firebase/api-call'
|
||||
import { transact } from 'web/lib/firebase/api'
|
||||
import { charities, Charity } from 'common/charity'
|
||||
import { useRouter } from 'next/router'
|
||||
import Custom404 from '../404'
|
||||
|
|
|
@ -6,7 +6,7 @@ import Textarea from 'react-expanding-textarea'
|
|||
import { Spacer } from 'web/components/layout/spacer'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { Contract, contractPath } from 'web/lib/firebase/contracts'
|
||||
import { createMarket } from 'web/lib/firebase/api-call'
|
||||
import { createMarket } from 'web/lib/firebase/api'
|
||||
import { FIXED_ANTE } from 'common/antes'
|
||||
import { InfoTooltip } from 'web/components/info-tooltip'
|
||||
import { Page } from 'web/components/page'
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useRouter } from 'next/router'
|
|||
import { useState } from 'react'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
import { Title } from 'web/components/title'
|
||||
import { claimManalink } from 'web/lib/firebase/api-call'
|
||||
import { claimManalink } from 'web/lib/firebase/api'
|
||||
import { useManalink } from 'web/lib/firebase/manalinks'
|
||||
import { ManalinkCard } from 'web/components/manalink-card'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Linkify } from 'web/components/linkify'
|
|||
import { Page } from 'web/components/page'
|
||||
import { Title } from 'web/components/title'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { createMarket } from 'web/lib/firebase/api-call'
|
||||
import { createMarket } from 'web/lib/firebase/api'
|
||||
import { contractPath } from 'web/lib/firebase/contracts'
|
||||
|
||||
type Prediction = {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Title } from 'web/components/title'
|
|||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { cleanDisplayName, cleanUsername } from 'common/util/clean-username'
|
||||
import { changeUserInfo } from 'web/lib/firebase/api-call'
|
||||
import { changeUserInfo } from 'web/lib/firebase/api'
|
||||
import { uploadImage } from 'web/lib/firebase/storage'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
|
|
Loading…
Reference in New Issue
Block a user