diff --git a/common/recommended-contracts.ts b/common/recommended-contracts.ts
deleted file mode 100644
index 3a6eca38..00000000
--- a/common/recommended-contracts.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-import { union, sum, sumBy, sortBy, groupBy, mapValues } from 'lodash'
-import { Bet } from './bet'
-import { Contract } from './contract'
-import { ClickEvent } from './tracking'
-import { filterDefined } from './util/array'
-import { addObjects } from './util/object'
-
-export const MAX_FEED_CONTRACTS = 75
-
-export const getRecommendedContracts = (
- contractsById: { [contractId: string]: Contract },
- yourBetOnContractIds: string[]
-) => {
- const contracts = Object.values(contractsById)
- const yourContracts = filterDefined(
- yourBetOnContractIds.map((contractId) => contractsById[contractId])
- )
-
- const yourContractIds = new Set(yourContracts.map((c) => c.id))
- const notYourContracts = contracts.filter((c) => !yourContractIds.has(c.id))
-
- const yourWordFrequency = contractsToWordFrequency(yourContracts)
- const otherWordFrequency = contractsToWordFrequency(notYourContracts)
- const words = union(
- Object.keys(yourWordFrequency),
- Object.keys(otherWordFrequency)
- )
-
- const yourWeightedFrequency = Object.fromEntries(
- words.map((word) => {
- const [yourFreq, otherFreq] = [
- yourWordFrequency[word] ?? 0,
- otherWordFrequency[word] ?? 0,
- ]
-
- const score = yourFreq / (yourFreq + otherFreq + 0.0001)
-
- return [word, score]
- })
- )
-
- // console.log(
- // 'your weighted frequency',
- // _.sortBy(_.toPairs(yourWeightedFrequency), ([, freq]) => -freq)
- // )
-
- const scoredContracts = contracts.map((contract) => {
- const wordFrequency = contractToWordFrequency(contract)
-
- const score = sumBy(Object.keys(wordFrequency), (word) => {
- const wordFreq = wordFrequency[word] ?? 0
- const weight = yourWeightedFrequency[word] ?? 0
- return wordFreq * weight
- })
-
- return {
- contract,
- score,
- }
- })
-
- return sortBy(scoredContracts, (scored) => -scored.score).map(
- (scored) => scored.contract
- )
-}
-
-const contractToText = (contract: Contract) => {
- const { description, question, tags, creatorUsername } = contract
- return `${creatorUsername} ${question} ${tags.join(' ')} ${description}`
-}
-
-const MAX_CHARS_IN_WORD = 100
-
-const getWordsCount = (text: string) => {
- const normalizedText = text.replace(/[^a-zA-Z]/g, ' ').toLowerCase()
- const words = normalizedText
- .split(' ')
- .filter((word) => word)
- .filter((word) => word.length <= MAX_CHARS_IN_WORD)
-
- const counts: { [word: string]: number } = {}
- for (const word of words) {
- if (counts[word]) counts[word]++
- else counts[word] = 1
- }
- return counts
-}
-
-const toFrequency = (counts: { [word: string]: number }) => {
- const total = sum(Object.values(counts))
- return mapValues(counts, (count) => count / total)
-}
-
-const contractToWordFrequency = (contract: Contract) =>
- toFrequency(getWordsCount(contractToText(contract)))
-
-const contractsToWordFrequency = (contracts: Contract[]) => {
- const frequencySum = contracts
- .map(contractToWordFrequency)
- .reduce(addObjects, {})
-
- return toFrequency(frequencySum)
-}
-
-export const getWordScores = (
- contracts: Contract[],
- contractViewCounts: { [contractId: string]: number },
- clicks: ClickEvent[],
- bets: Bet[]
-) => {
- const contractClicks = groupBy(clicks, (click) => click.contractId)
- const contractBets = groupBy(bets, (bet) => bet.contractId)
-
- const yourContracts = contracts.filter(
- (c) =>
- contractViewCounts[c.id] || contractClicks[c.id] || contractBets[c.id]
- )
- const yourTfIdf = calculateContractTfIdf(yourContracts)
-
- const contractWordScores = mapValues(yourTfIdf, (wordsTfIdf, contractId) => {
- const viewCount = contractViewCounts[contractId] ?? 0
- const clickCount = contractClicks[contractId]?.length ?? 0
- const betCount = contractBets[contractId]?.length ?? 0
-
- const factor =
- -1 * Math.log(viewCount + 1) +
- 10 * Math.log(betCount + clickCount / 4 + 1)
-
- return mapValues(wordsTfIdf, (tfIdf) => tfIdf * factor)
- })
-
- const wordScores = Object.values(contractWordScores).reduce(addObjects, {})
- const minScore = Math.min(...Object.values(wordScores))
- const maxScore = Math.max(...Object.values(wordScores))
- const normalizedWordScores = mapValues(
- wordScores,
- (score) => (score - minScore) / (maxScore - minScore)
- )
-
- // console.log(
- // 'your word scores',
- // _.sortBy(_.toPairs(normalizedWordScores), ([, score]) => -score).slice(0, 100),
- // _.sortBy(_.toPairs(normalizedWordScores), ([, score]) => -score).slice(-100)
- // )
-
- return normalizedWordScores
-}
-
-export function getContractScore(
- contract: Contract,
- wordScores: { [word: string]: number }
-) {
- if (Object.keys(wordScores).length === 0) return 1
-
- const wordFrequency = contractToWordFrequency(contract)
- const score = sumBy(Object.keys(wordFrequency), (word) => {
- const wordFreq = wordFrequency[word] ?? 0
- const weight = wordScores[word] ?? 0
- return wordFreq * weight
- })
-
- return score
-}
-
-// Caluculate Term Frequency-Inverse Document Frequency (TF-IDF):
-// https://medium.datadriveninvestor.com/tf-idf-in-natural-language-processing-8db8ef4a7736
-function calculateContractTfIdf(contracts: Contract[]) {
- const contractFreq = contracts.map((c) => contractToWordFrequency(c))
- const contractWords = contractFreq.map((freq) => Object.keys(freq))
-
- const wordsCount: { [word: string]: number } = {}
- for (const words of contractWords) {
- for (const word of words) {
- wordsCount[word] = (wordsCount[word] ?? 0) + 1
- }
- }
-
- const wordIdf = mapValues(wordsCount, (count) =>
- Math.log(contracts.length / count)
- )
- const contractWordsTfIdf = contractFreq.map((wordFreq) =>
- mapValues(wordFreq, (freq, word) => freq * wordIdf[word])
- )
- return Object.fromEntries(
- contracts.map((c, i) => [c.id, contractWordsTfIdf[i]])
- )
-}
diff --git a/web/components/feed/category-selector.tsx b/web/components/feed/category-selector.tsx
deleted file mode 100644
index a39f7402..00000000
--- a/web/components/feed/category-selector.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-import clsx from 'clsx'
-import { PencilIcon } from '@heroicons/react/outline'
-import { union, difference } from 'lodash'
-
-import { Row } from '../layout/row'
-import { CATEGORIES, category, CATEGORY_LIST } from '../../../common/categories'
-import { Modal } from '../layout/modal'
-import { Col } from '../layout/col'
-import { useState } from 'react'
-import { updateUser, User } from 'web/lib/firebase/users'
-import { Checkbox } from '../checkbox'
-import { track } from 'web/lib/service/analytics'
-
-export function CategorySelector(props: {
- category: string
- setCategory: (category: string) => void
- className?: string
-}) {
- const { className, category, setCategory } = props
-
- return (
-
-
- {
- setCategory('all')
- }}
- />
-
- {
- setCategory('following')
- }}
- />
-
- {CATEGORY_LIST.map((cat) => (
- {
- setCategory(cat)
- }}
- />
- ))}
-
- )
-}
-
-function CategoryButton(props: {
- category: string
- isFollowed: boolean
- toggle: () => void
- className?: string
-}) {
- const { toggle, category, isFollowed, className } = props
-
- return (
-
- {category}
-
- )
-}
-
-export function EditCategoriesButton(props: {
- user: User
- className?: string
-}) {
- const { user, className } = props
- const [isOpen, setIsOpen] = useState(false)
-
- return (
- {
- setIsOpen(true)
- track('edit categories button')
- }}
- >
-
- Categories
-
-
- )
-}
-
-function CategorySelectorModal(props: {
- user: User
- isOpen: boolean
- setIsOpen: (isOpen: boolean) => void
-}) {
- const { user, isOpen, setIsOpen } = props
- const followedCategories =
- user?.followedCategories === undefined
- ? CATEGORY_LIST
- : user.followedCategories
-
- const selectAll =
- user.followedCategories === undefined ||
- followedCategories.length < CATEGORY_LIST.length
-
- return (
-
-
-
-
- {CATEGORY_LIST.map((cat) => (
- {
- updateUser(user.id, {
- followedCategories: checked
- ? difference(followedCategories, [cat])
- : union([cat], followedCategories),
- })
- }}
- />
- ))}
-
-
-
- )
-}
diff --git a/web/components/tags-input.tsx b/web/components/tags-input.tsx
deleted file mode 100644
index dd8a2f1d..00000000
--- a/web/components/tags-input.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import clsx from 'clsx'
-import { useState } from 'react'
-import { parseWordsAsTags } from 'common/util/parse'
-import { Contract, updateContract } from 'web/lib/firebase/contracts'
-import { Col } from './layout/col'
-import { Row } from './layout/row'
-import { TagsList } from './tags-list'
-import { MAX_TAG_LENGTH } from 'common/contract'
-
-export function TagsInput(props: { contract: Contract; className?: string }) {
- const { contract, className } = props
- const { tags } = contract
-
- const [tagText, setTagText] = useState('')
- const newTags = parseWordsAsTags(`${tags.join(' ')} ${tagText}`)
-
- const [isSubmitting, setIsSubmitting] = useState(false)
-
- const updateTags = async () => {
- setIsSubmitting(true)
- await updateContract(contract.id, {
- tags: newTags,
- lowercaseTags: newTags.map((tag) => tag.toLowerCase()),
- })
- setIsSubmitting(false)
- setTagText('')
- }
-
- return (
-
-
-
-
- setTagText(e.target.value || '')}
- onKeyDown={(e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault()
- updateTags()
- }
- }}
- />
-
-
-
- )
-}
diff --git a/web/components/tags-list.tsx b/web/components/tags-list.tsx
deleted file mode 100644
index a13bcd35..00000000
--- a/web/components/tags-list.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import clsx from 'clsx'
-import { CATEGORIES, category } from '../../common/categories'
-import { Col } from './layout/col'
-
-import { Row } from './layout/row'
-import { SiteLink } from './site-link'
-
-function Hashtag(props: { tag: string; noLink?: boolean }) {
- const { tag, noLink } = props
- const category = CATEGORIES[tag.replace('#', '').toLowerCase() as category]
-
- const body = (
-
- {category ? '#' + category : tag}
-
- )
-
- if (noLink) return body
- return (
-
- {body}
-
- )
-}
-
-export function TagsList(props: {
- tags: string[]
- className?: string
- noLink?: boolean
- noLabel?: boolean
- label?: string
-}) {
- const { tags, className, noLink, noLabel, label } = props
- return (
-
- {!noLabel && {label || 'Tags'}
}
- {tags.map((tag) => (
-
- ))}
-
- )
-}
-
-export function FoldTag(props: { fold: { slug: string; name: string } }) {
- const { fold } = props
- const { slug, name } = fold
-
- return (
-
-
- {name}
-
-
- )
-}
-
-export function FoldTagList(props: {
- folds: { slug: string; name: string }[]
- noLabel?: boolean
- className?: string
-}) {
- const { folds, noLabel, className } = props
- return (
-
- {!noLabel && Communities
}
-
- {folds.length > 0 && (
- <>
- {folds.map((fold) => (
-
- ))}
- >
- )}
-
-
- )
-}
diff --git a/web/hooks/use-algo-feed.ts b/web/hooks/use-algo-feed.ts
deleted file mode 100644
index e195936f..00000000
--- a/web/hooks/use-algo-feed.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useState, useEffect } from 'react'
-import type { feed } from 'common/feed'
-import { useTimeSinceFirstRender } from './use-time-since-first-render'
-import { trackLatency } from 'web/lib/firebase/tracking'
-import { User } from 'common/user'
-import { getCategoryFeeds, getUserFeed } from 'web/lib/firebase/users'
-import {
- getRecentBetsAndComments,
- getTopWeeklyContracts,
-} from 'web/lib/firebase/contracts'
-
-export const useAlgoFeed = (
- user: User | null | undefined,
- category: string
-) => {
- const [allFeed, setAllFeed] = useState()
- const [categoryFeeds, setCategoryFeeds] = useState<{ [x: string]: feed }>()
-
- const getTime = useTimeSinceFirstRender()
-
- useEffect(() => {
- if (user) {
- getUserFeed(user.id).then((feed) => {
- if (feed.length === 0) {
- getDefaultFeed().then((feed) => setAllFeed(feed))
- } else setAllFeed(feed)
-
- trackLatency(user.id, 'feed', getTime())
- console.log('"all" feed load time', getTime())
- })
-
- getCategoryFeeds(user.id).then((feeds) => {
- setCategoryFeeds(feeds)
- console.log('category feeds load time', getTime())
- })
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [user?.id])
-
- const feed = category === 'all' ? allFeed : categoryFeeds?.[category]
-
- return feed
-}
-
-const getDefaultFeed = async () => {
- const contracts = await getTopWeeklyContracts()
- const feed = await Promise.all(
- contracts.map((c) => getRecentBetsAndComments(c))
- )
- return feed
-}
diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts
index b31b8d04..243a453a 100644
--- a/web/lib/firebase/contracts.ts
+++ b/web/lib/firebase/contracts.ts
@@ -22,7 +22,6 @@ import { createRNG, shuffle } from 'common/util/random'
import { getCpmmProbability } from 'common/calculate-cpmm'
import { formatMoney, formatPercent } from 'common/util/format'
import { DAY_MS } from 'common/util/time'
-import { MAX_FEED_CONTRACTS } from 'common/recommended-contracts'
import { Bet } from 'common/bet'
import { Comment } from 'common/comment'
import { ENV_CONFIG } from 'common/envs/constants'
@@ -285,16 +284,6 @@ export async function getContractsBySlugs(slugs: string[]) {
return sortBy(data, (contract) => -1 * contract.volume24Hours)
}
-const topWeeklyQuery = query(
- contracts,
- where('isResolved', '==', false),
- orderBy('volume7Days', 'desc'),
- limit(MAX_FEED_CONTRACTS)
-)
-export async function getTopWeeklyContracts() {
- return await getValues(topWeeklyQuery)
-}
-
const closingSoonQuery = query(
contracts,
where('isResolved', '==', false),
diff --git a/web/lib/firebase/users.ts b/web/lib/firebase/users.ts
index ac0eb099..6cfee163 100644
--- a/web/lib/firebase/users.ts
+++ b/web/lib/firebase/users.ts
@@ -14,20 +14,10 @@ import {
onSnapshot,
} from 'firebase/firestore'
import { getAuth } from 'firebase/auth'
-import { ref, getStorage, uploadBytes, getDownloadURL } from 'firebase/storage'
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth'
-import { zip } from 'lodash'
import { app, db } from './init'
import { PortfolioMetrics, PrivateUser, User } from 'common/user'
-import {
- coll,
- getValue,
- getValues,
- listenForValue,
- listenForValues,
-} from './utils'
-import { feed } from 'common/feed'
-import { CATEGORY_LIST } from 'common/categories'
+import { coll, getValues, listenForValue, listenForValues } from './utils'
import { safeLocalStorage } from '../util/local'
import { filterDefined } from 'common/util/array'
import { addUserToGroupViaId } from 'web/lib/firebase/groups'
@@ -202,20 +192,6 @@ export async function firebaseLogout() {
await auth.signOut()
}
-const storage = getStorage(app)
-// Example: uploadData('avatars/ajfi8iejsf.png', data)
-export async function uploadData(
- path: string,
- data: ArrayBuffer | Blob | Uint8Array
-) {
- const uploadRef = ref(storage, path)
- // Uploaded files should be cached for 1 day, then revalidated
- // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
- const metadata = { cacheControl: 'public, max-age=86400, must-revalidate' }
- await uploadBytes(uploadRef, data, metadata)
- return await getDownloadURL(uploadRef)
-}
-
export async function listUsers(userIds: string[]) {
if (userIds.length > 10) {
throw new Error('Too many users requested at once; Firestore limits to 10')
@@ -263,25 +239,6 @@ export function getUsers() {
return getValues(users)
}
-export async function getUserFeed(userId: string) {
- const feedDoc = doc(privateUsers, userId, 'cache', 'feed')
- const userFeed = await getValue<{
- feed: feed
- }>(feedDoc)
- return userFeed?.feed ?? []
-}
-
-export async function getCategoryFeeds(userId: string) {
- const cacheCollection = collection(privateUsers, userId, 'cache')
- const feedData = await Promise.all(
- CATEGORY_LIST.map((category) =>
- getValue<{ feed: feed }>(doc(cacheCollection, `feed-${category}`))
- )
- )
- const feeds = feedData.map((data) => data?.feed ?? [])
- return Object.fromEntries(zip(CATEGORY_LIST, feeds) as [string, feed][])
-}
-
export async function follow(userId: string, followedUserId: string) {
const followDoc = doc(collection(users, userId, 'follows'), followedUserId)
await setDoc(followDoc, {
diff --git a/web/pages/tag/[tag].tsx b/web/pages/tag/[tag].tsx
deleted file mode 100644
index f2554f49..00000000
--- a/web/pages/tag/[tag].tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useRouter } from 'next/router'
-import { useUser } from 'web/hooks/use-user'
-import { ContractSearch } from '../../components/contract-search'
-import { Page } from '../../components/page'
-import { Title } from '../../components/title'
-
-export default function TagPage() {
- const router = useRouter()
- const user = useUser()
- const { tag } = router.query as { tag: string }
- if (!router.isReady) return
-
- return (
-
-
-
-
- )
-}