diff --git a/web/components/contracts-list.tsx b/web/components/contracts-list.tsx
index b40c926a..07c052b0 100644
--- a/web/components/contracts-list.tsx
+++ b/web/components/contracts-list.tsx
@@ -16,6 +16,7 @@ import { UserLink } from './user-page'
import { Linkify } from './linkify'
import { Col } from './layout/col'
import { SiteLink } from './site-link'
+import { parseTags } from '../lib/util/parse'
export function ContractDetails(props: { contract: Contract }) {
const { contract } = props
@@ -162,9 +163,64 @@ export function CreatorContractsGrid(props: { contracts: Contract[] }) {
)
}
+export function TagContractsGrid(props: { contracts: Contract[] }) {
+ const { contracts } = props
+
+ const contractTags = _.flatMap(contracts, (contract) =>
+ parseTags(contract.question + ' ' + contract.description).map((tag) => ({
+ tag,
+ contract,
+ }))
+ )
+ const groupedByTag = _.groupBy(contractTags, ({ tag }) => tag)
+ const byTag = _.mapValues(groupedByTag, (contractTags) =>
+ contractTags.map(({ contract }) => contract)
+ )
+ const tags = _.sortBy(Object.keys(byTag), (tag) =>
+ _.sumBy(byTag[tag], (contract) => -1 * compute(contract).truePool)
+ )
+
+ return (
+
+ {tags.map((tag) => {
+ return (
+
+
+ #{tag}
+
+
+
+ {byTag[tag]
+ .slice(0, MAX_GROUPED_CONTRACTS_DISPLAYED)
+ .map((contract) => (
+
+ ))}
+
+
+ {byTag[tag].length > MAX_GROUPED_CONTRACTS_DISPLAYED ? (
+
+ e.stopPropagation()}
+ >
+ See all
+
+
+ ) : (
+
+ )}
+
+ )
+ })}
+
+ )
+}
+
const MAX_CONTRACTS_DISPLAYED = 99
-type Sort = 'creator' | 'createdTime' | 'pool' | 'resolved' | 'all'
+type Sort = 'creator' | 'tag' | 'createdTime' | 'pool' | 'resolved' | 'all'
export function SearchableGrid(props: {
contracts: Contract[]
defaultSort?: Sort
@@ -189,7 +245,7 @@ export function SearchableGrid(props: {
if (sort === 'createdTime' || sort === 'resolved' || sort === 'all') {
matches.sort((a, b) => b.createdTime - a.createdTime)
- } else if (sort === 'pool' || sort === 'creator') {
+ } else if (sort === 'pool' || sort === 'creator' || sort === 'tag') {
matches.sort((a, b) => compute(b).truePool - compute(a).truePool)
}
@@ -224,13 +280,16 @@ export function SearchableGrid(props: {
) : (
)}
+
- {!byOneCreator && (sort === 'creator' || sort === 'resolved') ? (
+ {sort === 'tag' ? (
+
+ ) : !byOneCreator && (sort === 'creator' || sort === 'resolved') ? (
) : (
diff --git a/web/lib/util/parse.ts b/web/lib/util/parse.ts
new file mode 100644
index 00000000..2857b7a1
--- /dev/null
+++ b/web/lib/util/parse.ts
@@ -0,0 +1,8 @@
+export function parseTags(text: string) {
+ const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
+ const matches = text.match(regex) || []
+ return matches.map((match) => {
+ const tag = match.trim().substring(1)
+ return tag
+ })
+}