Skip to content

Commit 0c90f66

Browse files
authored
Merge pull request #146 from medyo/develop
New version
2 parents 38d2e98 + 25e1ec4 commit 0c90f66

File tree

9 files changed

+112
-17
lines changed

9 files changed

+112
-17
lines changed

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(95vw / 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 - 14px * 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/components/Elements/ChipsSet/chipset.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,11 @@
4040
background-color: var(--chip-active-background);
4141
color: var(--chip-active-text);
4242
}
43+
44+
.chipsSet.alternative-color .chip {
45+
background-color: var(--card-action-button-background);
46+
}
47+
.chipsSet.alternative-color .chip.active {
48+
background-color: var(--card-active-action-button-background);
49+
color: var(--card-active-action-button-color);
50+
}

src/components/Layout/ScrollCardsNavigator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const ScrollCardsNavigator = () => {
3434
}, [])
3535

3636
useLayoutEffect(() => {
37+
setLeftButtonVisible(false)
3738
scrollBarContainer.current = document.querySelector('.Cards')
3839
}, [])
3940

@@ -48,7 +49,6 @@ export const ScrollCardsNavigator = () => {
4849
}, [handleKeyboardKeys])
4950

5051
useEffect(() => {
51-
setLeftButtonVisible(false)
5252
setRightButtonVisible(cards.length > maxCardsPerRow)
5353
}, [cards])
5454

src/features/settings/components/SettingsModal.tsx

Lines changed: 52 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'
@@ -12,19 +13,21 @@ import {
1213
identifyUserLanguages,
1314
identifyUserLinksInNewTab,
1415
identifyUserListingMode,
16+
identifyUserMaxVisibleCards,
1517
identifyUserSearchEngine,
1618
identifyUserTheme,
1719
trackLanguageAdd,
1820
trackLanguageRemove,
1921
trackListingModeSelect,
22+
trackMaxVisibleCardsChange,
2023
trackSearchEngineSelect,
2124
trackSourceAdd,
2225
trackSourceRemove,
2326
trackTabTarget,
2427
trackThemeSelect,
2528
} from 'src/lib/analytics'
2629
import { useUserPreferences } from 'src/stores/preferences'
27-
import { SearchEngineType, SelectedCard } from 'src/types'
30+
import { Option, SearchEngineType, SelectedCard } from 'src/types'
2831
import { RssSetting } from './RssSetting'
2932
import './settings.css'
3033

@@ -48,8 +51,10 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
4851
listingMode,
4952
theme,
5053
searchEngine,
54+
maxVisibleCards,
5155
setTheme,
5256
setListingMode,
57+
setMaxVisibleCards,
5358
setSearchEngine,
5459
setOpenLinksNewTab,
5560
setCards,
@@ -144,6 +149,15 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
144149
identifyUserTheme(newTheme)
145150
}
146151

152+
const onMaxVisibleCardsChange = (selectedChips: Option[]) => {
153+
if (selectedChips.length) {
154+
const maxVisibleCards = parseInt(selectedChips[0].value)
155+
setMaxVisibleCards(maxVisibleCards)
156+
identifyUserMaxVisibleCards(maxVisibleCards)
157+
trackMaxVisibleCardsChange(maxVisibleCards)
158+
}
159+
}
160+
147161
return (
148162
<ReactModal
149163
isOpen={showSettings}
@@ -215,6 +229,43 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
215229

216230
<RssSetting setSelectedCards={setSelectedCards} />
217231

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

src/lib/analytics.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ enum Verbs {
3838
FINISH = 'Finish',
3939
SKIP = 'Skip',
4040
DRAG = 'Drag',
41+
Change = 'Change',
4142
}
4243

4344
export enum Attributes {
@@ -60,6 +61,7 @@ export enum Attributes {
6061
SOURCE_TAGS = 'Source Tags',
6162
CAMPAIGN_ID = 'Campaign Id',
6263
OCCUPATION = 'Occupation',
64+
MAX_VISIBLE_CARDS = 'Max Visible Cards',
6365
}
6466

6567
const _SEP_ = ' '
@@ -82,6 +84,7 @@ export const setupIdentification = () => {
8284
listingMode,
8385
openLinksNewTab,
8486
searchEngine,
87+
maxVisibleCards
8588
} = useUserPreferences.getState()
8689

8790
identifyUserProperty(Attributes.RESOLUTION, getScreenResolution())
@@ -91,6 +94,7 @@ export const setupIdentification = () => {
9194
identifyUserListingMode(listingMode)
9295
identifyUserSearchEngine(searchEngine)
9396
identifyUserLinksInNewTab(openLinksNewTab)
97+
identifyUserMaxVisibleCards(maxVisibleCards)
9498
if (onboardingResult?.title) {
9599
identifyUserOccupation(onboardingResult.title)
96100
}
@@ -291,6 +295,14 @@ export const trackPageDrag = () => {
291295
})
292296
}
293297

298+
export const trackMaxVisibleCardsChange = (maxVisibleCards: number) => {
299+
trackEvent({
300+
object: Objects.CARD,
301+
verb: Verbs.Change,
302+
attributes: {[Attributes.MAX_VISIBLE_CARDS]: maxVisibleCards}
303+
})
304+
}
305+
294306
// Identification
295307

296308
export const identifyUserLanguages = (languages: string[]) => {
@@ -317,7 +329,9 @@ export const identifyUserLinksInNewTab = (enabled: boolean) => {
317329
export const identifyUserOccupation = (occupation: string) => {
318330
identifyUserProperty(Attributes.OCCUPATION, occupation)
319331
}
320-
332+
export const identifyUserMaxVisibleCards = (maxVisibleCards: number) => {
333+
identifyUserProperty(Attributes.MAX_VISIBLE_CARDS, maxVisibleCards)
334+
}
321335

322336
// Private functions
323337
type trackEventProps = {
@@ -365,13 +379,13 @@ const trackEvent = ({ object, verb, attributes }: trackEventProps) => {
365379
}
366380
}
367381

368-
const identifyUserProperty = (attributes: Attributes, value: string | string[]) => {
382+
const identifyUserProperty = (attributes: Attributes, value: string | number | string[]) => {
369383
try {
370384
let formatedValue
371385
if (Array.isArray(value)) {
372386
formatedValue = value.filter(Boolean).map((item) => item.toLowerCase())
373387
} else {
374-
formatedValue = value.toLowerCase()
388+
formatedValue = typeof value === "string" ? value.toLowerCase() : value.toString()
375389
}
376390

377391
if (isDevelopment()) {

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)