Merge pull request #3004 from omnivore-app/feat/web-label-flair
Label flair for the web
This commit is contained in:
29
packages/web/components/elements/icons/FavoriteFlairIcon.tsx
Normal file
29
packages/web/components/elements/icons/FavoriteFlairIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class FavoriteFlairIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M5.39136 2.61542C5.9814 2.51489 6.58648 2.54805 7.162 2.71247C7.73752 2.87689 8.2688 3.16837 8.71669 3.56542L8.74136 3.58742L8.76402 3.56742C9.19149 3.19229 9.69404 2.91262 10.2381 2.74708C10.7822 2.58154 11.3554 2.53392 11.9194 2.60742L12.0834 2.63142C12.7942 2.75416 13.4587 3.06686 14.0063 3.5364C14.554 4.00594 14.9645 4.61485 15.1943 5.29865C15.4242 5.98244 15.4648 6.71566 15.312 7.42067C15.1591 8.12568 14.8184 8.77622 14.326 9.30342L14.206 9.42676L14.174 9.45409L9.20736 14.3734C9.09274 14.4869 8.94089 14.5549 8.77995 14.565C8.619 14.575 8.45986 14.5264 8.33203 14.4281L8.26936 14.3734L3.27402 9.42542C2.74484 8.91053 2.36849 8.25921 2.18666 7.5436C2.00484 6.82799 2.02463 6.07602 2.24384 5.37097C2.46305 4.66591 2.87313 4.03529 3.42867 3.54894C3.9842 3.06259 4.66351 2.73949 5.39136 2.61542Z"
|
||||
fill="#F8023B"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
44
packages/web/components/elements/icons/FeedFlairIcon.tsx
Normal file
44
packages/web/components/elements/icons/FeedFlairIcon.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class FeedFlairIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M8.73861 3.23242L3.40527 5.89909L8.73861 8.56576L14.0719 5.89909L8.73861 3.23242Z"
|
||||
fill="#FF7B03"
|
||||
stroke="#FF7B03"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.40527 8.56641L8.73861 11.2331L14.0719 8.56641"
|
||||
stroke="#FF7B03"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.40527 11.2324L8.73861 13.8991L14.0719 11.2324"
|
||||
stroke="#FF7B03"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class NewsletterFlairIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M15.4056 5.58984V11.8998C15.4056 12.41 15.2107 12.9009 14.8607 13.272C14.5108 13.6432 14.0322 13.8666 13.5229 13.8965L13.4056 13.8998H4.07227C3.56213 13.8999 3.07126 13.705 2.70009 13.355C2.32893 13.005 2.10553 12.5264 2.0756 12.0172L2.07227 11.8998V5.58984L8.36893 9.78784L8.44627 9.83184C8.5374 9.87637 8.6375 9.89952 8.73893 9.89952C8.84037 9.89952 8.94046 9.87637 9.0316 9.83184L9.10893 9.78784L15.4056 5.58984Z"
|
||||
fill="#007AFF"
|
||||
/>
|
||||
<path
|
||||
d="M13.4053 3.23242C14.1253 3.23242 14.7567 3.61242 15.1087 4.18376L8.73865 8.43042L2.36865 4.18376C2.53581 3.91227 2.76546 3.68469 3.03846 3.52001C3.31145 3.35533 3.61987 3.25833 3.93799 3.23709L4.07199 3.23242H13.4053Z"
|
||||
fill="#007AFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
29
packages/web/components/elements/icons/PinnedFlairIcon.tsx
Normal file
29
packages/web/components/elements/icons/PinnedFlairIcon.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class PinnedFlairIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M10.814 2.70621L10.8767 2.76154L14.5434 6.42821C14.6557 6.54119 14.7239 6.69069 14.7356 6.84963C14.7472 7.00856 14.7016 7.16641 14.6069 7.29457C14.5121 7.42273 14.3747 7.51273 14.2193 7.54825C14.064 7.58377 13.901 7.56247 13.76 7.48821L11.6454 9.60221L10.696 12.1335C10.671 12.2003 10.6355 12.2627 10.5907 12.3182L10.544 12.3715L9.54403 13.3715C9.42915 13.4862 9.27637 13.5551 9.11435 13.5651C8.95234 13.5752 8.79222 13.5258 8.66403 13.4262L8.60069 13.3709L6.73869 11.5095L4.21003 14.0375C4.09005 14.1571 3.92907 14.2265 3.75977 14.2317C3.59047 14.2369 3.42555 14.1774 3.29851 14.0654C3.17146 13.9534 3.09182 13.7972 3.07576 13.6286C3.0597 13.4599 3.10842 13.2915 3.21203 13.1575L3.26736 13.0949L5.79536 10.5662L3.93403 8.70421C3.81924 8.58941 3.75029 8.43668 3.7401 8.27466C3.72991 8.11265 3.77919 7.95248 3.87869 7.82421L3.93403 7.76154L4.93403 6.76154C4.98433 6.71106 5.0424 6.66897 5.10603 6.63688L5.17136 6.60888L7.70203 5.65888L9.81603 3.54554C9.74394 3.41076 9.72035 3.25531 9.74922 3.10521C9.77809 2.95512 9.85765 2.8195 9.97459 2.72107C10.0915 2.62265 10.2387 2.56739 10.3916 2.56457C10.5444 2.56174 10.6935 2.61218 10.814 2.70621Z"
|
||||
fill="#3D3D3D"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/* eslint-disable functional/no-class */
|
||||
/* eslint-disable functional/no-this-expression */
|
||||
import { IconProps } from './IconProps'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
export class RecommendedFlairIcon extends React.Component<IconProps> {
|
||||
render() {
|
||||
const size = (this.props.size || 26).toString()
|
||||
const color = (this.props.color || '#2A2A2A').toString()
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M9.4056 2.56641C9.91574 2.56638 10.4066 2.76129 10.7778 3.11126C11.1489 3.46123 11.3723 3.93981 11.4023 4.44907L11.4056 4.56641V7.23307H12.7389C13.229 7.23299 13.7021 7.41286 14.0683 7.73853C14.4345 8.0642 14.6684 8.513 14.7256 8.99974L14.7356 9.11574L14.7389 9.23307L14.7256 9.36374L14.0549 12.7184C13.8009 13.8024 13.0536 14.5824 12.1816 14.5717L12.0723 14.5664H6.73893C6.57564 14.5664 6.41804 14.5064 6.29602 14.3979C6.17399 14.2894 6.09604 14.1399 6.07693 13.9777L6.07227 13.8997L6.07293 7.54241C6.07305 7.4255 6.10391 7.31068 6.16242 7.20946C6.22093 7.10824 6.30502 7.02419 6.40627 6.96574C6.69055 6.80155 6.93001 6.56987 7.10349 6.29116C7.27697 6.01245 7.37913 5.6953 7.40093 5.36774L7.4056 5.23307V4.56641C7.4056 4.03597 7.61631 3.52727 7.99139 3.15219C8.36646 2.77712 8.87517 2.56641 9.4056 2.56641Z"
|
||||
fill="#FEC43F"
|
||||
/>
|
||||
<path
|
||||
d="M4.07227 7.23242C4.23555 7.23244 4.39316 7.29239 4.51518 7.4009C4.6372 7.5094 4.71516 7.65892 4.73427 7.82109L4.73893 7.89909V13.8991C4.73891 14.0624 4.67896 14.22 4.57046 14.342C4.46195 14.464 4.31243 14.542 4.15027 14.5611L4.07227 14.5658H3.4056C3.06921 14.5659 2.74522 14.4388 2.49857 14.2101C2.25191 13.9814 2.10083 13.6679 2.0756 13.3324L2.07227 13.2324V8.56576C2.07216 8.22937 2.1992 7.90538 2.42793 7.65872C2.65666 7.41207 2.97016 7.26098 3.3056 7.23576L3.4056 7.23242H4.07227Z"
|
||||
fill="#FEC43F"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,13 @@ import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { ChangeEvent, useMemo } from 'react'
|
||||
import { LibraryItemNode } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Box, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { RecommendedFlairIcon } from '../../elements/icons/RecommendedFlairIcon'
|
||||
import { PinnedFlairIcon } from '../../elements/icons/PinnedFlairIcon'
|
||||
import { FavoriteFlairIcon } from '../../elements/icons/FavoriteFlairIcon'
|
||||
import { NewsletterFlairIcon } from '../../elements/icons/NewsletterFlairIcon'
|
||||
import { FeedFlairIcon } from '../../elements/icons/FeedFlairIcon'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
@ -83,6 +89,32 @@ const shouldHideUrl = (url: string): boolean => {
|
||||
return false
|
||||
}
|
||||
|
||||
export const FLAIR_ICON_NAMES = [
|
||||
'favorite',
|
||||
'pinned',
|
||||
'recommended',
|
||||
'newsletter',
|
||||
'feed',
|
||||
'rss',
|
||||
]
|
||||
|
||||
const flairIconForLabel = (label: Label): JSX.Element | undefined => {
|
||||
switch (label.name.toLocaleLowerCase()) {
|
||||
case 'favorite':
|
||||
return <FavoriteFlairIcon />
|
||||
case 'pinned':
|
||||
return <PinnedFlairIcon />
|
||||
case 'recommended':
|
||||
return <RecommendedFlairIcon />
|
||||
case 'newsletter':
|
||||
return <NewsletterFlairIcon />
|
||||
case 'rss':
|
||||
case 'feed':
|
||||
return <FeedFlairIcon />
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export const siteName = (
|
||||
originalArticleUrl: string,
|
||||
itemUrl: string
|
||||
@ -114,7 +146,10 @@ export function LibraryItemMetadata(
|
||||
}, [props.item.highlights])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HStack css={{ gap: '5px' }}>
|
||||
{props.item.labels?.map((label) => {
|
||||
return flairIconForLabel(label)
|
||||
})}
|
||||
{timeAgo(props.item.savedAt)}
|
||||
{` `}
|
||||
{props.item.wordsCount ?? 0 > 0
|
||||
@ -126,7 +161,7 @@ export function LibraryItemMetadata(
|
||||
{highlightCount > 0
|
||||
? ` • ${highlightCount} highlight${highlightCount > 1 ? 's' : ''}`
|
||||
: null}
|
||||
</Box>
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
siteName,
|
||||
TitleStyle,
|
||||
MenuStyle,
|
||||
FLAIR_ICON_NAMES,
|
||||
} from './LibraryCardStyles'
|
||||
import { sortedLabels } from '../../../lib/labelsSort'
|
||||
import { LibraryHoverActions } from './LibraryHoverActions'
|
||||
@ -285,9 +286,14 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => {
|
||||
marginLeft: '-4px', // offset because the chips have margin
|
||||
}}
|
||||
>
|
||||
{sortedLabels(props.item.labels).map(({ name, color }, index) => (
|
||||
<LabelChip key={index} text={name || ''} color={color} />
|
||||
))}
|
||||
{sortedLabels(props.item.labels)
|
||||
.filter(
|
||||
({ name }) =>
|
||||
FLAIR_ICON_NAMES.indexOf(name.toLocaleLowerCase()) == -1
|
||||
)
|
||||
.map(({ name, color }, index) => (
|
||||
<LabelChip key={index} text={name || ''} color={color} />
|
||||
))}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
siteName,
|
||||
TitleStyle,
|
||||
MenuStyle,
|
||||
FLAIR_ICON_NAMES,
|
||||
} from './LibraryCardStyles'
|
||||
import { sortedLabels } from '../../../lib/labelsSort'
|
||||
import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu'
|
||||
@ -354,9 +355,14 @@ export function LibraryListCardContent(
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
{sortedLabels(props.item.labels).map(({ name, color }, index) => (
|
||||
<LabelChip key={index} text={name || ''} color={color} />
|
||||
))}
|
||||
{sortedLabels(props.item.labels)
|
||||
.filter(
|
||||
({ name }) =>
|
||||
FLAIR_ICON_NAMES.indexOf(name.toLocaleLowerCase()) == -1
|
||||
)
|
||||
.map(({ name, color }, index) => (
|
||||
<LabelChip key={index} text={name || ''} color={color} />
|
||||
))}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
Reference in New Issue
Block a user