diff --git a/common/contract.ts b/common/contract.ts index 8bdab6fe..c414a332 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -139,7 +139,7 @@ export const OUTCOME_TYPES = [ ] as const export const MAX_QUESTION_LENGTH = 480 -export const MAX_DESCRIPTION_LENGTH = 10000 +export const MAX_DESCRIPTION_LENGTH = 16000 export const MAX_TAG_LENGTH = 60 export const CPMM_MIN_POOL_QTY = 0.01 diff --git a/common/util/array.ts b/common/util/array.ts index d81edba1..2ad86843 100644 --- a/common/util/array.ts +++ b/common/util/array.ts @@ -1,3 +1,19 @@ export function filterDefined(array: (T | null | undefined)[]) { return array.filter((item) => item !== null && item !== undefined) as T[] } + +export function buildArray( + ...params: (T | T[] | false | undefined | null)[] +) { + const array: T[] = [] + + for (const el of params) { + if (Array.isArray(el)) { + array.push(...el) + } else if (el) { + array.push(el) + } + } + + return array +} diff --git a/functions/src/scripts/set-avatar-cache-headers.ts b/functions/src/scripts/set-avatar-cache-headers.ts new file mode 100644 index 00000000..676ec62d --- /dev/null +++ b/functions/src/scripts/set-avatar-cache-headers.ts @@ -0,0 +1,27 @@ +import { initAdmin } from './script-init' +import { log } from '../utils' + +const app = initAdmin() +const ONE_YEAR_SECS = 60 * 60 * 24 * 365 +const AVATAR_EXTENSION_RE = /\.(gif|tiff|jpe?g|png|webp)$/i + +const processAvatars = async () => { + const storage = app.storage() + const bucket = storage.bucket(`${app.options.projectId}.appspot.com`) + const [files] = await bucket.getFiles({ prefix: 'user-images' }) + log(`${files.length} avatar images to process.`) + for (const file of files) { + if (AVATAR_EXTENSION_RE.test(file.name)) { + log(`Updating metadata for ${file.name}.`) + await file.setMetadata({ + cacheControl: `public, max-age=${ONE_YEAR_SECS}`, + }) + } else { + log(`Skipping ${file.name} because it probably isn't an avatar.`) + } + } +} + +if (require.main === module) { + processAvatars().catch((e) => console.error(e)) +} diff --git a/web/components/avatar.tsx b/web/components/avatar.tsx index 0436d61c..19b6066e 100644 --- a/web/components/avatar.tsx +++ b/web/components/avatar.tsx @@ -47,14 +47,21 @@ export function Avatar(props: { ) } -export function EmptyAvatar(props: { size?: number; multi?: boolean }) { - const { size = 8, multi } = props +export function EmptyAvatar(props: { + className?: string + size?: number + multi?: boolean +}) { + const { className, size = 8, multi } = props const insize = size - 3 const Icon = multi ? UsersIcon : UserIcon return (
diff --git a/web/components/bet-panel.tsx b/web/components/bet-panel.tsx index c0f7ff94..9c572e0c 100644 --- a/web/components/bet-panel.tsx +++ b/web/components/bet-panel.tsx @@ -484,6 +484,8 @@ function LimitOrderPanel(props: { setIsSubmitting(false) setWasSubmitted(true) setBetAmount(undefined) + setLowLimitProb(undefined) + setHighLimitProb(undefined) if (onBuySuccess) onBuySuccess() }) diff --git a/web/components/comments-list.tsx b/web/components/comments-list.tsx index 2a467f6d..de4ea67f 100644 --- a/web/components/comments-list.tsx +++ b/web/components/comments-list.tsx @@ -65,7 +65,7 @@ function ProfileComment(props: { comment: Comment; className?: string }) { />{' '}

- + ) diff --git a/web/components/contract-search.tsx b/web/components/contract-search.tsx index 4de232fc..372c04da 100644 --- a/web/components/contract-search.tsx +++ b/web/components/contract-search.tsx @@ -2,6 +2,7 @@ import algoliasearch from 'algoliasearch/lite' import { Contract } from 'common/contract' +import { User } from 'common/user' import { QuerySortOptions, Sort, @@ -14,7 +15,6 @@ import { import { Row } from './layout/row' import { useEffect, useMemo, useState } from 'react' import { ENV, IS_PRIVATE_MANIFOLD } from 'common/envs/constants' -import { useUser } from 'web/hooks/use-user' import { useFollows } from 'web/hooks/use-follows' import { track, trackCallback } from 'web/lib/service/analytics' import ContractSearchFirestore from 'web/pages/contract-search-firestore' @@ -49,6 +49,7 @@ export const DEFAULT_SORT = 'score' type filter = 'personal' | 'open' | 'closed' | 'resolved' | 'all' export function ContractSearch(props: { + user: User | null | undefined querySortOptions?: { defaultFilter?: filter } & QuerySortOptions additionalFilter?: { creatorId?: string @@ -68,6 +69,7 @@ export function ContractSearch(props: { headerClassName?: string }) { const { + user, querySortOptions, additionalFilter, onContractClick, @@ -79,7 +81,6 @@ export function ContractSearch(props: { headerClassName, } = props - const user = useUser() const memberGroups = (useMemberGroups(user?.id) ?? []).filter( (group) => !NEW_USER_GROUP_SLUGS.includes(group.slug) ) @@ -234,7 +235,7 @@ export function ContractSearch(props: { if (newFilter === filter) return setFilter(newFilter) setPage(0) - trackCallback('select search filter', { filter: newFilter }) + track('select search filter', { filter: newFilter }) } const selectSort = (newSort: Sort) => { @@ -242,7 +243,7 @@ export function ContractSearch(props: { setPage(0) setSort(newSort) - track('select sort', { sort: newSort }) + track('select search sort', { sort: newSort }) } if (IS_PRIVATE_MANIFOLD || process.env.NEXT_PUBLIC_FIREBASE_EMULATE) { @@ -267,6 +268,7 @@ export function ContractSearch(props: { type="text" value={query} onChange={(e) => updateQuery(e.target.value)} + onBlur={trackCallback('search', { query })} placeholder={showPlaceHolder ? `Search ${filter} markets` : ''} className="input input-bordered w-full" /> @@ -347,7 +349,6 @@ export function ContractSearch(props: { void - hasMore: boolean + loadMore?: () => void showTime?: ShowTime onContractClick?: (contract: Contract) => void overrideGridClassName?: string @@ -31,7 +30,6 @@ export function ContractsGrid(props: { const { contracts, showTime, - hasMore, loadMore, onContractClick, overrideGridClassName, @@ -39,16 +37,15 @@ export function ContractsGrid(props: { highlightOptions, } = props const { hideQuickBet, hideGroupLink } = cardHideOptions || {} - const { contractIds, highlightClassName } = highlightOptions || {} - const [elem, setElem] = useState(null) - const isBottomVisible = useIsVisible(elem) - - useEffect(() => { - if (isBottomVisible && hasMore) { - loadMore() - } - }, [isBottomVisible, hasMore, loadMore]) + const onVisibilityUpdated = useCallback( + (visible) => { + if (visible && loadMore) { + loadMore() + } + }, + [loadMore] + ) if (contracts === undefined) { return @@ -92,16 +89,23 @@ export function ContractsGrid(props: { /> ))} -
+ ) } -export function CreatorContractsList(props: { creator: User }) { - const { creator } = props +export function CreatorContractsList(props: { + user: User | null | undefined + creator: User +}) { + const { user, creator } = props return (