Kill tag and categories related component UI
This commit is contained in:
parent
ea03b43560
commit
75e08a3c4c
|
@ -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 (
|
|
||||||
<Row
|
|
||||||
className={clsx(
|
|
||||||
'carousel mr-2 items-center space-x-2 space-y-2 overflow-x-scroll pb-4 sm:flex-wrap',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
<CategoryButton
|
|
||||||
key="all"
|
|
||||||
category="All"
|
|
||||||
isFollowed={category === 'all'}
|
|
||||||
toggle={() => {
|
|
||||||
setCategory('all')
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CategoryButton
|
|
||||||
key="following"
|
|
||||||
category="Following"
|
|
||||||
isFollowed={category === 'following'}
|
|
||||||
toggle={() => {
|
|
||||||
setCategory('following')
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{CATEGORY_LIST.map((cat) => (
|
|
||||||
<CategoryButton
|
|
||||||
key={cat}
|
|
||||||
category={CATEGORIES[cat as category].split(' ')[0]}
|
|
||||||
isFollowed={cat === category}
|
|
||||||
toggle={() => {
|
|
||||||
setCategory(cat)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CategoryButton(props: {
|
|
||||||
category: string
|
|
||||||
isFollowed: boolean
|
|
||||||
toggle: () => void
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { toggle, category, isFollowed, className } = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
className,
|
|
||||||
'rounded-full border-2 px-4 py-1 shadow-md hover:bg-gray-200',
|
|
||||||
'cursor-pointer select-none',
|
|
||||||
isFollowed ? 'border-gray-300 bg-gray-300' : 'bg-white'
|
|
||||||
)}
|
|
||||||
onClick={toggle}
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-500">{category}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EditCategoriesButton(props: {
|
|
||||||
user: User
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { user, className } = props
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
className,
|
|
||||||
'btn btn-sm btn-ghost cursor-pointer gap-2 whitespace-nowrap text-sm normal-case text-gray-700'
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
setIsOpen(true)
|
|
||||||
track('edit categories button')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PencilIcon className="inline h-4 w-4" />
|
|
||||||
Categories
|
|
||||||
<CategorySelectorModal
|
|
||||||
user={user}
|
|
||||||
isOpen={isOpen}
|
|
||||||
setIsOpen={setIsOpen}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<Modal open={isOpen} setOpen={setIsOpen}>
|
|
||||||
<Col className="rounded bg-white p-6">
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-outline mb-4 self-start normal-case"
|
|
||||||
onClick={() => {
|
|
||||||
if (selectAll) {
|
|
||||||
updateUser(user.id, {
|
|
||||||
followedCategories: CATEGORY_LIST,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
updateUser(user.id, {
|
|
||||||
followedCategories: [],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Select {selectAll ? 'all' : 'none'}
|
|
||||||
</button>
|
|
||||||
<Col className="grid w-full grid-cols-2 gap-4">
|
|
||||||
{CATEGORY_LIST.map((cat) => (
|
|
||||||
<Checkbox
|
|
||||||
className="col-span-1"
|
|
||||||
key={cat}
|
|
||||||
label={CATEGORIES[cat as category].split(' ')[0]}
|
|
||||||
checked={followedCategories.includes(cat)}
|
|
||||||
toggle={(checked) => {
|
|
||||||
updateUser(user.id, {
|
|
||||||
followedCategories: checked
|
|
||||||
? difference(followedCategories, [cat])
|
|
||||||
: union([cat], followedCategories),
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Col>
|
|
||||||
</Col>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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 (
|
|
||||||
<Col className={clsx('gap-4', className)}>
|
|
||||||
<TagsList tags={newTags} noLabel />
|
|
||||||
|
|
||||||
<Row className="items-center gap-4">
|
|
||||||
<input
|
|
||||||
style={{ maxWidth: 150 }}
|
|
||||||
placeholder="Type a tag..."
|
|
||||||
className="input input-sm input-bordered resize-none"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
value={tagText}
|
|
||||||
maxLength={MAX_TAG_LENGTH}
|
|
||||||
onChange={(e) => setTagText(e.target.value || '')}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
e.preventDefault()
|
|
||||||
updateTags()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button className="btn btn-xs btn-outline" onClick={updateTags}>
|
|
||||||
Save tags
|
|
||||||
</button>
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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 = (
|
|
||||||
<div className={clsx('', !noLink && 'cursor-pointer')}>
|
|
||||||
<span className="text-sm">{category ? '#' + category : tag} </span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (noLink) return body
|
|
||||||
return (
|
|
||||||
<SiteLink href={`/tag/${tag.substring(1)}`} className="flex items-center">
|
|
||||||
{body}
|
|
||||||
</SiteLink>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TagsList(props: {
|
|
||||||
tags: string[]
|
|
||||||
className?: string
|
|
||||||
noLink?: boolean
|
|
||||||
noLabel?: boolean
|
|
||||||
label?: string
|
|
||||||
}) {
|
|
||||||
const { tags, className, noLink, noLabel, label } = props
|
|
||||||
return (
|
|
||||||
<Row className={clsx('flex-wrap items-center gap-2', className)}>
|
|
||||||
{!noLabel && <div className="mr-1">{label || 'Tags'}</div>}
|
|
||||||
{tags.map((tag) => (
|
|
||||||
<Hashtag
|
|
||||||
key={tag}
|
|
||||||
tag={tag.startsWith('#') ? tag : `#${tag}`}
|
|
||||||
noLink={noLink}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FoldTag(props: { fold: { slug: string; name: string } }) {
|
|
||||||
const { fold } = props
|
|
||||||
const { slug, name } = fold
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SiteLink href={`/fold/${slug}`} className="flex items-center">
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
'rounded-full border-2 bg-white px-4 py-1 shadow-md',
|
|
||||||
'cursor-pointer'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-500">{name}</span>
|
|
||||||
</div>
|
|
||||||
</SiteLink>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FoldTagList(props: {
|
|
||||||
folds: { slug: string; name: string }[]
|
|
||||||
noLabel?: boolean
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { folds, noLabel, className } = props
|
|
||||||
return (
|
|
||||||
<Col className="gap-2">
|
|
||||||
{!noLabel && <div className="mr-1 text-gray-500">Communities</div>}
|
|
||||||
<Row className={clsx('flex-wrap items-center gap-2', className)}>
|
|
||||||
{folds.length > 0 && (
|
|
||||||
<>
|
|
||||||
{folds.map((fold) => (
|
|
||||||
<FoldTag key={fold.slug} fold={fold} />
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user