Skip to content

Commit 58f79e9

Browse files
authored
Merge pull request #282 from medyo/develop
New Version
2 parents 8cbbcb4 + d48e730 commit 58f79e9

File tree

12 files changed

+229
-34
lines changed

12 files changed

+229
-34
lines changed

src/assets/App.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ a {
201201
box-sizing: border-box;
202202
scroll-snap-align: start;
203203
width: 100%;
204+
position: relative;
204205
}
205206

206207
.blockContent {
@@ -467,7 +468,8 @@ a {
467468
padding: 0;
468469
pointer-events: all;
469470
text-align: center;
470-
transition: opacity 0.3s ease, right 0.3s ease;
471+
transition: opacity 0.3s ease, right 0.3s ease, transform 0.1s ease, background-color 0.15s ease,
472+
color 0.15s ease;
471473
width: 28px;
472474
margin-bottom: 6px;
473475
margin-right: 6px;
@@ -543,6 +545,10 @@ a {
543545
.rowTitle:hover {
544546
color: var(--primary-hover-text-color);
545547
}
548+
549+
.blockRow.isRead {
550+
opacity: 0.3;
551+
}
546552
.titleWithCover {
547553
display: block;
548554
width: 100%;

src/components/Elements/CardLink/CardLink.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const CardLink = ({
1414
link,
1515
children,
1616
className = '',
17-
appendRef = false,
17+
appendRef = true,
1818
analyticsAttributes,
1919
}: CardLinkProps) => {
2020
return (

src/components/Elements/CardWithActions/CardItemWithActions.tsx

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
import clsx from 'clsx'
12
import React, { useCallback, useEffect, useState } from 'react'
2-
import { BiBookmarkMinus, BiBookmarkPlus, BiShareAlt } from 'react-icons/bi'
3+
import { BiBookmarkMinus, BiBookmarkPlus, BiCheckDouble, BiShareAlt } from 'react-icons/bi'
34
import { ShareModal } from 'src/features/shareModal'
45
import { ShareModalData } from 'src/features/shareModal/types'
5-
import { Attributes, trackLinkBookmark, trackLinkUnBookmark } from 'src/lib/analytics'
6+
import {
7+
Attributes,
8+
trackLinkBookmark,
9+
trackLinkUnBookmark,
10+
trackMarkAsRead,
11+
} from 'src/lib/analytics'
612
import { useBookmarks } from 'src/stores/bookmarks'
13+
import { useReadPosts } from 'src/stores/readPosts'
14+
import { useShallow } from 'zustand/shallow'
715

816
type CardItemWithActionsProps = {
917
item: {
@@ -32,11 +40,36 @@ export const CardItemWithActions = ({
3240
const [shareModalData, setShareModalData] = useState<ShareModalData>()
3341

3442
const { bookmarkPost, unbookmarkPost, userBookmarks } = useBookmarks()
43+
const { isRead, markAsRead, markAsUnread } = useReadPosts(
44+
useShallow((state) => ({
45+
markAsRead: state.markAsRead,
46+
markAsUnread: state.markAsUnread,
47+
isRead: state.readPostIds.includes(item.id),
48+
}))
49+
)
3550
const [isBookmarked, setIsBookmarked] = useState(
3651
userBookmarks.some((bm) => bm.source === source && bm.url === item.url)
3752
)
3853

39-
const onBookmarkClick = useCallback(() => {
54+
const onMarkAsReadClicked = useCallback(() => {
55+
if (isRead) {
56+
markAsUnread(item.id)
57+
} else {
58+
markAsRead(item.id)
59+
}
60+
61+
if (isRead) {
62+
const analyticsAttrs = {
63+
[Attributes.TRIGERED_FROM]: 'card',
64+
[Attributes.TITLE]: item.title,
65+
[Attributes.LINK]: item.url,
66+
[Attributes.SOURCE]: source,
67+
}
68+
trackMarkAsRead(analyticsAttrs)
69+
}
70+
}, [isRead, item.id])
71+
72+
const onBookmarkClicked = useCallback(() => {
4073
const itemToBookmark = {
4174
title: item.title,
4275
url: item.url,
@@ -70,7 +103,7 @@ export const CardItemWithActions = ({
70103
setShareModalData({ title: item.title, link: item.url, source: source })
71104
}, [item.title, item.url, source])
72105
return (
73-
<div key={item.id} className="blockRow">
106+
<div key={item.id} className={clsx('blockRow', { isRead })}>
74107
<ShareModal
75108
showModal={setShareModalData !== undefined}
76109
closeModal={() => setShareModalData(undefined)}
@@ -95,11 +128,18 @@ export const CardItemWithActions = ({
95128
{showBookmarkAction && (
96129
<button
97130
className={`blockActionButton ${isBookmarked ? 'active' : ''}`}
98-
onClick={onBookmarkClick}
131+
onClick={onBookmarkClicked}
99132
aria-label="Bookmark item">
100133
{!isBookmarked ? <BiBookmarkPlus /> : <BiBookmarkMinus />}
101134
</button>
102135
)}
136+
137+
<button
138+
className={`blockActionButton ${isRead ? 'active' : ''}`}
139+
onClick={onMarkAsReadClicked}
140+
aria-label={isRead ? 'Mark as unread' : 'Mark as read'}>
141+
<BiCheckDouble />
142+
</button>
103143
</div>
104144
</div>
105145
)

src/components/Elements/UserTags/UserTags.tsx

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,65 @@
1-
import { useCallback } from 'react'
1+
import { useCallback, useMemo } from 'react'
2+
import { FaGlobe } from 'react-icons/fa'
23
import { TiPlus } from 'react-icons/ti'
34
import { Link } from 'react-router-dom'
45
import { useUserPreferences } from 'src/stores/preferences'
56
import { useShallow } from 'zustand/shallow'
67

78
export const UserTags = () => {
8-
const { cards, userSelectedTags, cardsSettings, setCardSettings } = useUserPreferences(
9-
useShallow((state) => ({
10-
cards: state.cards,
11-
userSelectedTags: state.userSelectedTags,
12-
cardsSettings: state.cardsSettings,
13-
setCardSettings: state.setCardSettings,
14-
}))
15-
)
9+
const { cards, userSelectedTags, cardsSettings, setCardSettings, clearCardSettingsLanguages } =
10+
useUserPreferences(
11+
useShallow((state) => ({
12+
cards: state.cards,
13+
userSelectedTags: state.userSelectedTags,
14+
cardsSettings: state.cardsSettings,
15+
setCardSettings: state.setCardSettings,
16+
clearCardSettingsLanguages: state.clearCardSettingsLanguages,
17+
}))
18+
)
1619

17-
const onTagClicked = useCallback((tagValue: string) => {
18-
cards.forEach((card) => {
19-
setCardSettings(card.name, {
20-
...cardsSettings[card.id],
21-
language: tagValue,
20+
const onTagClicked = useCallback(
21+
(tagValue: string) => {
22+
if (tagValue === 'all') {
23+
clearCardSettingsLanguages()
24+
return
25+
}
26+
27+
cards.forEach((card) => {
28+
setCardSettings(card.name, {
29+
...cardsSettings[card.id],
30+
language: tagValue,
31+
})
2232
})
23-
})
24-
}, [])
33+
},
34+
[cards, cardsSettings, setCardSettings]
35+
)
36+
37+
const tagsList = useMemo(() => {
38+
const tags = userSelectedTags.map((tag) => ({
39+
label: tag.label,
40+
value: tag.value,
41+
icon: undefined,
42+
}))
43+
44+
if (tags.length === 0) {
45+
return tags
46+
}
47+
48+
return [
49+
{
50+
label: 'All',
51+
value: 'all',
52+
icon: <FaGlobe className="tagIcon" />,
53+
},
54+
...tags,
55+
]
56+
}, [userSelectedTags])
2557

2658
return (
2759
<div className="tags">
28-
{userSelectedTags.map((tag, index) => (
60+
{tagsList.map((tag, index) => (
2961
<button key={index} className="tag tagHoverable" onClick={() => onTagClicked(tag.value)}>
30-
{tag.label}
62+
{tag.icon} {tag.label}
3163
</button>
3264
))}
3365
<Link to="/settings/topics" className="tag tagHoverable" aria-label="Open settings">

src/components/List/ListComponent.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { memo, ReactNode, useMemo } from 'react'
22
import { Placeholder } from 'src/components/placeholders'
33
import { MAX_ITEMS_PER_CARD } from 'src/config'
4+
import { useUserPreferences } from 'src/stores/preferences'
5+
import { useReadPosts } from 'src/stores/readPosts'
46

57
type PlaceholdersProps = {
68
placeholder: ReactNode
@@ -42,13 +44,28 @@ export function ListComponent<T extends any>(props: ListComponentPropsType<T>) {
4244
limit = MAX_ITEMS_PER_CARD,
4345
} = props
4446

47+
const { showReadPosts } = useUserPreferences()
48+
const readPostIdSet = useReadPosts((state) => state.readPostIdSet)
49+
50+
const filteredItems = useMemo(() => {
51+
if (!items || items.length === 0) {
52+
return []
53+
}
54+
55+
if (showReadPosts) {
56+
return items
57+
}
58+
59+
return items.filter((item: any) => !readPostIdSet.has(item.id))
60+
}, [items, readPostIdSet, showReadPosts])
61+
4562
const sortedData = useMemo(() => {
46-
if (!items || items.length == 0) return []
47-
if (!sortBy) return items
63+
if (!filteredItems || filteredItems.length == 0) return []
64+
if (!sortBy) return filteredItems
4865

4966
const result = sortFn
50-
? [...items].sort(sortFn)
51-
: [...items].sort((a, b) => {
67+
? [...filteredItems].sort(sortFn)
68+
: [...filteredItems].sort((a, b) => {
5269
const aVal = a[sortBy]
5370
const bVal = b[sortBy]
5471
if (typeof aVal === 'number' && typeof bVal === 'number') return bVal - aVal
@@ -57,7 +74,7 @@ export function ListComponent<T extends any>(props: ListComponentPropsType<T>) {
5774
})
5875

5976
return result
60-
}, [sortBy, sortFn, items])
77+
}, [sortBy, sortFn, filteredItems])
6178

6279
const enrichedItems = useMemo(() => {
6380
if (!sortedData || sortedData.length === 0) {
@@ -93,5 +110,15 @@ export function ListComponent<T extends any>(props: ListComponentPropsType<T>) {
93110
)
94111
}
95112

113+
if (items && items.length > 0 && filteredItems.length === 0) {
114+
return (
115+
<div className="errorMsg">
116+
<span></span>
117+
<b>You're all caught up!</b>
118+
<p>Check back later for fresh content.</p>
119+
</div>
120+
)
121+
}
122+
96123
return <>{enrichedItems}</>
97124
}

src/features/adv/components/AdvBanner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ export const AdvBanner = ({ feedDisplay = false, loadingState, onAdLoaded }: Adv
9898
<img
9999
src={ad.imageUrl}
100100
alt={ad.title}
101-
height={!feedDisplay ? '100' : '200'}
102-
width={!feedDisplay ? '130' : '260'}
101+
height={!feedDisplay ? '120' : '200'}
102+
width={!feedDisplay ? '156' : '260'}
103103
style={{ border: 0 }}
104104
/>
105105
</a>

src/features/cards/components/producthuntCard/ArticleItem.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const ArticleItem = ({ item, analyticsTag }: BaseItemPropsType<Product>) => {
1818
<div className="phContent">
1919
<CardLink
2020
link={item.url}
21-
appendRef={false}
2221
analyticsAttributes={{
2322
[Attributes.POINTS]: item.votes_count,
2423
[Attributes.TRIGERED_FROM]: 'card',

src/features/settings/components/GeneralSettings/GeneralSettings.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ export const GeneralSettings = () => {
2525
listingMode,
2626
theme,
2727
maxVisibleCards,
28+
showReadPosts,
2829
setTheme,
2930
setListingMode,
3031
setMaxVisibleCards,
3132
setOpenLinksNewTab,
33+
setShowReadPosts,
3234
} = useUserPreferences()
3335

3436
const onOpenLinksNewTabChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -52,6 +54,10 @@ export const GeneralSettings = () => {
5254
identifyUserTheme(newTheme)
5355
}
5456

57+
const onShowReadPostsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
58+
setShowReadPosts(e.target.checked)
59+
}
60+
5561
return (
5662
<SettingsContentLayout
5763
title="General Settings"
@@ -77,6 +83,13 @@ export const GeneralSettings = () => {
7783
</div>
7884
</div>
7985

86+
<div className="settingRow">
87+
<p className="settingTitle">Display read posts</p>
88+
<div className="settingContent">
89+
<Toggle checked={showReadPosts} icons={false} onChange={onShowReadPostsChange} />
90+
</div>
91+
</div>
92+
8093
<div className="settingRow">
8194
<p className="settingTitle">Compact mode</p>
8295
<div className="settingContent">

src/lib/analytics.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum Verbs {
3737
TARGET = 'Target',
3838
BOOKMARK = 'Bookmark',
3939
UNBOOKMARK = 'Unbookmark',
40+
MARK_AS_READ = 'Mark as Read',
4041
REMOVE = 'Remove',
4142
START = 'Start',
4243
FINISH = 'Finish',
@@ -226,6 +227,14 @@ export const trackLinkUnBookmark = (attributes: { [P: string]: string }) => {
226227
})
227228
}
228229

230+
export const trackMarkAsRead = (attributes: { [P: string]: string }) => {
231+
trackEvent({
232+
object: Objects.LINK,
233+
verb: Verbs.MARK_AS_READ,
234+
attributes,
235+
})
236+
}
237+
229238
export const trackLinkOpen = (attributes: { [P: string]: string | number | undefined }) => {
230239
trackEvent({
231240
object: Objects.LINK,

0 commit comments

Comments
 (0)