Skip to content

Commit ad39ea0

Browse files
committed
implement: customizing the number of visible cards
1 parent 793d366 commit ad39ea0

6 files changed

Lines changed: 81 additions & 13 deletions

File tree

src/App.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ function App() {
1919
const [showSideBar, setShowSideBar] = useState(false)
2020
const [showSettings, setShowSettings] = useState(false)
2121
const [showOnboarding, setShowOnboarding] = useState(true)
22-
const { onboardingCompleted, firstSeenDate, markOnboardingAsCompleted } = useUserPreferences()
22+
const { onboardingCompleted, firstSeenDate, markOnboardingAsCompleted, maxVisibleCards } =
23+
useUserPreferences()
2324

2425
useLayoutEffect(() => {
2526
if (!onboardingCompleted && getAppVersion() <= '1.15.9') {
@@ -31,6 +32,10 @@ function App() {
3132
// eslint-disable-next-line react-hooks/exhaustive-deps
3233
}, [onboardingCompleted, firstSeenDate])
3334

35+
useEffect(() => {
36+
document.documentElement.style.setProperty('--max-visible-cards', maxVisibleCards)
37+
}, [maxVisibleCards])
38+
3439
useEffect(() => {
3540
setupAnalytics()
3641
setupIdentification()

src/assets/App.css

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,18 +1043,21 @@ Producthunt item
10431043
.floatingFilter {
10441044
display: block;
10451045
}
1046+
.block {
1047+
width: 100vw;
1048+
}
10461049
}
10471050
/* Small devices (portrait tablets and large phones, 600px and up) */
10481051
@media only screen and (min-width: 600px) {
10491052
.block {
1050-
width: calc(96vw / 2);
1053+
width: calc(96vw / min(2, var(--max-visible-cards)));
10511054
}
10521055
}
10531056

10541057
/* Medium devices (landscape tablets, 768px and up) */
10551058
@media only screen and (min-width: 768px) {
10561059
.block {
1057-
width: calc(96vw / 3);
1060+
width: calc(96vw / min(3, var(--max-visible-cards)));
10581061
}
10591062
.searchBarInput {
10601063
width: 200px;
@@ -1064,7 +1067,7 @@ Producthunt item
10641067
/* Large devices (desktops, 992px and up)*/
10651068
@media only screen and (min-width: 992px) {
10661069
.block {
1067-
width: calc(96vw / 3);
1070+
width: calc(96vw / min(3, var(--max-visible-cards)));
10681071
}
10691072

10701073
.searchBarInput {
@@ -1075,14 +1078,14 @@ Producthunt item
10751078
/* X-Large devices (large desktops, 1200px and up)*/
10761079
@media only screen and (min-width: 1200px) {
10771080
.block {
1078-
width: calc(96vw / 4);
1081+
width: calc(96vw / min(4, var(--max-visible-cards)));
10791082
}
10801083
}
10811084

10821085
/* XX-Large devices (larger desktops, 1400px and up)*/
10831086
@media only screen and (min-width: 1400px) {
10841087
.block {
1085-
width: calc(96vw / 4);
1088+
width: calc(96vw / min(4, var(--max-visible-cards)));
10861089
}
10871090
}
10881091

@@ -1093,7 +1096,9 @@ Producthunt item
10931096
padding: 0;
10941097
}
10951098
.block {
1096-
width: calc((1800px - 12px * 4) / 4);
1099+
width: calc(
1100+
(1800px - 12px * min(5, var(--max-visible-cards))) / min(5, var(--max-visible-cards))
1101+
);
10971102
}
10981103
}
10991104

@@ -1120,3 +1125,6 @@ Producthunt item
11201125
position: relative;
11211126
vertical-align: middle;
11221127
}
1128+
.noMargin {
1129+
margin: 0 !important;
1130+
}

src/assets/variables.css

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
src: url('fonts/nunito/nunito-semibold.woff2') format('woff2');
1111
}
1212

13+
html {
14+
--max-visible-cards: 4;
15+
}
16+
1317
html.dark {
1418
--app-name-text-color: #fff;
1519
--background-color: #0d1116;
@@ -89,7 +93,7 @@ html.dark {
8993
--settings-input-border-color: #3a4553;
9094
--settings-input-border-focus-color: #6b7b90;
9195
--settings-input-placeholder-color: #42474e;
92-
--settings-input-text-color: #fff
96+
--settings-input-text-color: #fff;
9397
}
9498

9599
html.light {
@@ -171,6 +175,5 @@ html.light {
171175
--settings-input-border-color: #e9ebec;
172176
--settings-input-border-focus-color: #c4d6df;
173177
--settings-input-placeholder-color: #97a6ad;
174-
--settings-input-text-color: #253b53
175-
178+
--settings-input-text-color: #253b53;
176179
}

src/components/Elements/ChipsSet/ChipsSet.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import clsx from 'clsx'
12
import { useState } from 'react'
23
import { Option } from 'src/types'
34
import './chipset.css'
4-
55
type ChipProps = {
66
option: Option
77
onSelect: (option: Option) => void
@@ -19,12 +19,14 @@ const Chip = ({ option, onSelect, active = false }: ChipProps) => {
1919
type ChangeAction = 'ADD' | 'REMOVE'
2020
type ChipsSetProps = {
2121
options: Option[]
22+
className?: string
2223
defaultValues?: string[]
2324
canSelectMultiple?: boolean
2425
onChange?: (action: ChangeAction, options: Option[]) => void
2526
}
2627

2728
export const ChipsSet = ({
29+
className,
2830
options,
2931
canSelectMultiple = false,
3032
onChange,
@@ -62,7 +64,7 @@ export const ChipsSet = ({
6264
}
6365

6466
return (
65-
<div className="chipsSet">
67+
<div className={clsx('chipsSet', className)}>
6668
{options.map((option) => {
6769
return (
6870
<Chip

src/features/settings/components/SettingsModal.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ReactModal from 'react-modal'
44
import Select, { ActionMeta, MultiValue, SingleValue } from 'react-select'
55
import Toggle from 'react-toggle'
66
import 'react-toggle/style.css'
7+
import { ChipsSet } from 'src/components/Elements'
78
import { Footer } from 'src/components/Layout'
89
import { SUPPORTED_CARDS, SUPPORTED_SEARCH_ENGINES, supportLink } from 'src/config'
910
import { Tag, useRemoteConfigStore } from 'src/features/remoteConfig'
@@ -24,7 +25,7 @@ import {
2425
trackThemeSelect,
2526
} from 'src/lib/analytics'
2627
import { useUserPreferences } from 'src/stores/preferences'
27-
import { SearchEngineType, SelectedCard } from 'src/types'
28+
import { Option, SearchEngineType, SelectedCard } from 'src/types'
2829
import { RssSetting } from './RssSetting'
2930
import './settings.css'
3031

@@ -48,8 +49,10 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
4849
listingMode,
4950
theme,
5051
searchEngine,
52+
maxVisibleCards,
5153
setTheme,
5254
setListingMode,
55+
setMaxVisibleCards,
5356
setSearchEngine,
5457
setOpenLinksNewTab,
5558
setCards,
@@ -144,6 +147,12 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
144147
identifyUserTheme(newTheme)
145148
}
146149

150+
const onCardsCountChange = (selectedChips: Option[]) => {
151+
if (selectedChips.length) {
152+
setMaxVisibleCards(parseInt(selectedChips[0].value))
153+
}
154+
}
155+
147156
return (
148157
<ReactModal
149158
isOpen={showSettings}
@@ -215,6 +224,43 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
215224

216225
<RssSetting setSelectedCards={setSelectedCards} />
217226

227+
<div className="settingRow">
228+
<p className="settingTitle">Max number of cards to display</p>
229+
<div className="settingContent">
230+
<ChipsSet
231+
className={'noMargin'}
232+
canSelectMultiple={false}
233+
options={[
234+
{
235+
label: '3 cards',
236+
value: '3',
237+
},
238+
{
239+
label: '4 cards',
240+
value: '4',
241+
},
242+
{
243+
label: '5 cards',
244+
value: '5',
245+
},
246+
{
247+
label: '6 cards',
248+
value: '6',
249+
},
250+
]}
251+
defaultValues={[maxVisibleCards.toString()]}
252+
onChange={(_, selectedChips) => {
253+
onCardsCountChange(selectedChips)
254+
}}
255+
/>
256+
257+
<p className="settingHint">
258+
To ensure a seamless experience, we may adjust the selected number to align with the
259+
resolution of your screen.
260+
</p>
261+
</div>
262+
</div>
263+
218264
<div className="settingRow">
219265
<p className="settingTitle">Dark Mode</p>
220266
<div className="settingContent">

src/stores/preferences.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type UserPreferencesState = {
1414
onboardingResult: Omit<Occupation, 'icon'> | null
1515
listingMode: ListingMode
1616
searchEngine: string
17+
maxVisibleCards: number
1718
cards: SelectedCard[]
1819
cardsSettings: Record<string, CardSettingsType>
1920
firstSeenDate: number
@@ -28,6 +29,7 @@ type UserPreferencesStoreActions = {
2829
setListingMode: (listingMode: ListingMode) => void
2930
setCards: (selectedCards: SelectedCard[]) => void
3031
setTags: (selectedTags: Tag[]) => void
32+
setMaxVisibleCards: (maxVisibleCards: number) => void
3133
setCardSettings: (card: string, settings: CardSettingsType) => void
3234
markOnboardingAsCompleted: (occupation: Omit<Occupation, 'icon'> | null) => void
3335
setUserCustomCards: (cards: SupportedCardType[]) => void
@@ -39,6 +41,7 @@ export const useUserPreferences = create(
3941
(set) => ({
4042
userSelectedTags: [],
4143
cardsSettings: {},
44+
maxVisibleCards: 4,
4245
theme: 'dark',
4346
onboardingCompleted: false,
4447
onboardingResult: null,
@@ -59,6 +62,7 @@ export const useUserPreferences = create(
5962
setOpenLinksNewTab: (openLinksNewTab: boolean) => set({ openLinksNewTab: openLinksNewTab }),
6063
setCards: (selectedCards: SelectedCard[]) => set({ cards: selectedCards }),
6164
setTags: (selectedTags: Tag[]) => set({ userSelectedTags: selectedTags }),
65+
setMaxVisibleCards: (maxVisibleCards: number) => set({ maxVisibleCards: maxVisibleCards }),
6266
initState: (newState: UserPreferencesState) =>
6367
set(() => {
6468
return { ...newState }

0 commit comments

Comments
 (0)