Skip to content

Commit 8653252

Browse files
authored
Merge pull request #253 from medyo/develop
Improve card, onboarding, and topic experience; add lazy loading, clickable tags, and new sources/filters #minor
2 parents 3d2ef77 + abf3eb1 commit 8653252

File tree

103 files changed

+2377
-1686
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+2377
-1686
lines changed

.github/workflows/develop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
run: yarn
3131

3232
- name: Build project
33-
run: yarn build:web
33+
run: yarn build:web --mode development
3434

3535
- name: Copy build to remote host
3636
uses: appleboy/[email protected]

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"@dnd-kit/core": "^6.3.1",
88
"@dnd-kit/sortable": "^10.0.0",
99
"@sentry/react": "^9.38.0",
10+
"@szhsin/react-menu": "^4.5.0",
1011
"@tanstack/query-async-storage-persister": "^5.8.3",
1112
"@tanstack/react-query": "^4.13.0",
1213
"@tanstack/react-query-persist-client": "^5.8.4",
@@ -33,7 +34,6 @@
3334
"react-share": "^4.4.1",
3435
"react-simple-toasts": "^6.1.0",
3536
"react-spinners": "^0.10.4",
36-
"react-spring-bottom-sheet": "^3.4.1",
3737
"react-toggle": "^4.1.1",
3838
"react-tooltip": "^4.2.21",
3939
"timeago.js": "^4.0.2",

script/build.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build() {
44
echo 'Building Hackertab...'
55
rm -rf dist
66
tsc
7-
vite build
7+
vite build "$@"
88
}
99

10-
build
10+
build "$@"

src/App.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import clsx from 'clsx'
2-
import { useEffect, useLayoutEffect, useState } from 'react'
2+
import { useEffect, useLayoutEffect } from 'react'
33
import { DNDLayout } from 'src/components/Layout'
44
import {
55
identifyAdvBlocked,
@@ -10,7 +10,6 @@ import {
1010
import { useUserPreferences } from 'src/stores/preferences'
1111
import { AppContentLayout } from './components/Layout'
1212
import { verifyAdvStatus } from './features/adv/utils/status'
13-
import { isWebOrExtensionVersion } from './utils/Environment'
1413
import { lazyImport } from './utils/lazyImport'
1514
const { OnboardingModal } = lazyImport(() => import('src/features/onboarding'), 'OnboardingModal')
1615

@@ -25,10 +24,9 @@ const intersectionCallback = (entries: IntersectionObserverEntry[]) => {
2524
}
2625

2726
export const App = () => {
28-
const [showOnboarding, setShowOnboarding] = useState(true)
2927
const {
30-
onboardingCompleted,
3128
maxVisibleCards,
29+
onboardingCompleted,
3230
setAdvStatus,
3331
isDNDModeActive,
3432
layout,
@@ -78,10 +76,7 @@ export const App = () => {
7876

7977
return (
8078
<>
81-
{!onboardingCompleted && isWebOrExtensionVersion() === 'extension' && (
82-
<OnboardingModal showOnboarding={showOnboarding} setShowOnboarding={setShowOnboarding} />
83-
)}
84-
79+
{!onboardingCompleted && <OnboardingModal />}
8580
<div
8681
className={clsx(
8782
'layoutLayers hideScrollBar',

src/assets/App.css

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ a {
3535
text-align: center;
3636
margin: auto;
3737
font-size: 16px;
38+
line-height: 24px;
3839
padding: 16px;
3940
}
4041

@@ -269,16 +270,94 @@ a {
269270
max-width: 180px;
270271
}
271272

272-
.blockHeader:hover .blockHeaderLink {
273+
.blockHeader:hover .blockHeaderLink,
274+
.blockHeader:hover .blockHeaderSettingsButton {
273275
opacity: 1;
274276
}
275277

278+
.menuItem {
279+
font-size: 1em;
280+
display: flex;
281+
flex-direction: row;
282+
font-family: 'nunito';
283+
column-gap: 8px;
284+
svg {
285+
font-size: 1.1em;
286+
}
287+
}
288+
289+
.szh-menu__header {
290+
margin-bottom: 6px;
291+
}
292+
.light {
293+
.menuItem {
294+
color: var(--card-header-text-color);
295+
}
296+
.szh-menu__item--disabled {
297+
color: var(--secondary-text-color);
298+
opacity: 0.6;
299+
cursor: not-allowed;
300+
}
301+
}
302+
303+
.dark {
304+
.szh-menu {
305+
background-color: var(--card-background-color);
306+
border-radius: 12px;
307+
border: 1px solid var(--card-border-color);
308+
box-shadow: 0 0 20px var(--card-border-color);
309+
}
310+
.szh-menu__item {
311+
color: var(--primary-text-color);
312+
}
313+
314+
.szh-menu__divider {
315+
background-color: var(--card-border-color);
316+
}
317+
318+
.szh-menu__item--hover {
319+
background-color: var(--app-name-text-color);
320+
color: var(--background-color);
321+
}
322+
.szh-menu__item--hover {
323+
background-color: var(--app-name-text-color);
324+
}
325+
.szh-menu__submenu {
326+
background-color: var(--card-background-color);
327+
328+
.szh-menu__item--hover {
329+
background-color: var(--app-name-text-color);
330+
color: var(--background-color);
331+
}
332+
}
333+
.szh-menu__item--disabled {
334+
color: var(--secondary-text-color);
335+
opacity: 0.6;
336+
cursor: not-allowed;
337+
}
338+
}
339+
340+
.blockHeaderSettingsButton {
341+
color: #96a2ae;
342+
display: flex;
343+
justify-content: center;
344+
opacity: 0;
345+
font-size: 1.2em;
346+
transition: opacity 0.2s linear;
347+
cursor: pointer;
348+
&:focus {
349+
cursor: pointer;
350+
opacity: 1;
351+
}
352+
}
353+
276354
.blockHeaderLink {
277355
align-items: center;
278356
color: #96a2ae;
279357
display: flex;
280358
justify-content: center;
281359
opacity: 0;
360+
font-size: 1.2em;
282361
transition: opacity 0.2s linear;
283362
}
284363

@@ -311,7 +390,13 @@ a {
311390
transform: rotate(3deg);
312391
opacity: 0.5;
313392
}
314-
393+
.blockHeaderHighlight {
394+
border-radius: 15px;
395+
padding: 1px 6px;
396+
border: 1px solid var(--chip-border-color);
397+
background-color: var(--chip-background);
398+
font-size: 0.9em;
399+
}
315400
.blockHeaderIcon {
316401
display: flex;
317402
height: 16px;
@@ -330,6 +415,11 @@ a {
330415
padding: 0 6px;
331416
text-transform: lowercase;
332417
color: white;
418+
419+
&.past {
420+
background-color: #dd5353;
421+
margin-right: 4px;
422+
}
333423
}
334424
.blockHeaderIcon img {
335425
display: block;
@@ -567,6 +657,10 @@ a {
567657
transition: opacity 0.3s ease-out 0.1s, transform 0.3s ease-out 0.1s,
568658
visibility 0.3s ease-out 0.1s;
569659
width: 100%;
660+
661+
button.tag {
662+
cursor: pointer;
663+
}
570664
}
571665

572666
.tag {
@@ -921,7 +1015,17 @@ Producthunt item
9211015
padding: 0 32px 0 48px;
9221016
width: 100%;
9231017
background-color: var(--card-header-background-color);
1018+
1019+
&.noShadow {
1020+
box-shadow: none;
1021+
}
1022+
&::placeholder {
1023+
color: var(--primary-text-color);
1024+
font-size: 0.9em;
1025+
opacity: 0.5;
1026+
}
9241027
}
1028+
9251029
.searchBarInput:focus {
9261030
outline: none;
9271031
}
@@ -1159,6 +1263,11 @@ Producthunt item
11591263
right: 16px;
11601264
width: 48px;
11611265
z-index: 2;
1266+
1267+
& svg {
1268+
color: white;
1269+
font-size: 24px;
1270+
}
11621271
}
11631272

11641273
.floatingFilterBottomSheet .title {

src/assets/icon_hackernoon.jpeg

7.33 KB
Loading
Lines changed: 59 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,78 @@
11
import clsx from 'clsx'
22
import React, { useEffect, useState } from 'react'
3-
import { BsBoxArrowInUpRight } from 'react-icons/bs'
4-
import { ref } from 'src/config'
53
import { AdvBanner } from 'src/features/adv'
6-
import { useRemoteConfigStore } from 'src/features/remoteConfig'
7-
import { useUserPreferences } from 'src/stores/preferences'
4+
import { DesktopBreakpoint } from 'src/providers/DesktopBreakpoint'
5+
import { MobileBreakpoint } from 'src/providers/MobileBreakpoint'
86
import { CardPropsType } from 'src/types'
97

108
type RootCardProps = CardPropsType & {
119
children: React.ReactNode
1210
titleComponent?: React.ReactNode
11+
settingsComponent?: React.ReactNode
1312
fullBlock?: boolean
1413
}
14+
export const Card = React.forwardRef<HTMLDivElement, RootCardProps>(
15+
(
16+
{
17+
meta,
18+
titleComponent,
19+
settingsComponent,
20+
className,
21+
withAds = false,
22+
children,
23+
fullBlock = false,
24+
knob,
25+
},
26+
ref
27+
) => {
28+
const { icon, label, badge } = meta
29+
const [canAdsLoad, setCanAdsLoad] = useState(true)
1530

16-
export const Card = ({
17-
meta,
18-
titleComponent,
19-
className,
20-
withAds = false,
21-
children,
22-
fullBlock = false,
23-
knob,
24-
}: RootCardProps) => {
25-
const { openLinksNewTab } = useUserPreferences()
26-
const { link, icon, label, badge } = meta
27-
const [canAdsLoad, setCanAdsLoad] = useState(true)
28-
const { adsConfig } = useRemoteConfigStore()
29-
30-
useEffect(() => {
31-
if (!adsConfig.enabled || !withAds) {
32-
return
33-
}
31+
useEffect(() => {
32+
if (!withAds) {
33+
return
34+
}
3435

35-
const handleClassChange = () => {
36-
if (document.documentElement.classList.contains('dndState')) {
37-
setCanAdsLoad(false)
38-
} else {
39-
setCanAdsLoad(true)
36+
const handleClassChange = () => {
37+
if (document.documentElement.classList.contains('dndState')) {
38+
setCanAdsLoad(false)
39+
} else {
40+
setCanAdsLoad(true)
41+
}
4042
}
41-
}
4243

43-
const observer = new MutationObserver(handleClassChange)
44-
observer.observe(document.documentElement, { attributes: true })
44+
const observer = new MutationObserver(handleClassChange)
45+
observer.observe(document.documentElement, { attributes: true })
4546

46-
return () => {
47-
observer.disconnect()
48-
}
49-
}, [withAds, adsConfig.enabled])
47+
return () => {
48+
observer.disconnect()
49+
}
50+
}, [withAds])
5051

51-
const handleHeaderLinkClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
52-
e.preventDefault()
53-
let url = `${link}?${ref}`
54-
window.open(url, openLinksNewTab ? '_blank' : '_self')
55-
}
52+
return (
53+
<div ref={ref} className={clsx('block', fullBlock && 'fullBlock', className)}>
54+
<MobileBreakpoint>
55+
{settingsComponent && <button className="floatingFilter">{settingsComponent}</button>}
56+
</MobileBreakpoint>
57+
<div className="blockHeader">
58+
{knob}
59+
<span className="blockHeaderIcon">{icon}</span> {titleComponent || label}{' '}
60+
<DesktopBreakpoint>
61+
{settingsComponent && (
62+
<span className="blockHeaderSettingsButton">{settingsComponent}</span>
63+
)}
64+
</DesktopBreakpoint>
65+
{badge && <span className="blockHeaderBadge">{badge}</span>}
66+
</div>
5667

57-
return (
58-
<div className={clsx('block', fullBlock && 'fullBlock', className)}>
59-
<div className="blockHeader">
60-
{knob}
61-
<span className="blockHeaderIcon">{icon}</span> {titleComponent || label}{' '}
62-
{link && (
63-
<a className="blockHeaderLink" href={link} onClick={handleHeaderLinkClick}>
64-
<BsBoxArrowInUpRight />
65-
</a>
68+
{canAdsLoad && withAds && (
69+
<div className="ad-wrapper blockRow">
70+
<AdvBanner />
71+
</div>
6672
)}
67-
{badge && <span className="blockHeaderBadge">{badge}</span>}
68-
</div>
6973

70-
{canAdsLoad && adsConfig.enabled && withAds && (
71-
<div className="ad-wrapper blockRow">
72-
<AdvBanner />
73-
</div>
74-
)}
75-
76-
<div className="blockContent scrollable">{children}</div>
77-
</div>
78-
)
79-
}
74+
<div className="blockContent scrollable">{children}</div>
75+
</div>
76+
)
77+
}
78+
)

0 commit comments

Comments
 (0)