Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ function App() {
const [showSideBar, setShowSideBar] = useState(false)
const [showSettings, setShowSettings] = useState(false)
const [showOnboarding, setShowOnboarding] = useState(true)
const { onboardingCompleted, firstSeenDate, markOnboardingAsCompleted } = useUserPreferences()
const { onboardingCompleted, firstSeenDate, markOnboardingAsCompleted, maxVisibleCards } =
useUserPreferences()

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

useEffect(() => {
document.documentElement.style.setProperty('--max-visible-cards', maxVisibleCards)
}, [maxVisibleCards])

useEffect(() => {
setupAnalytics()
setupIdentification()
Expand Down
20 changes: 14 additions & 6 deletions src/assets/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -1043,18 +1043,21 @@ Producthunt item
.floatingFilter {
display: block;
}
.block {
width: 100vw;
}
}
/* Small devices (portrait tablets and large phones, 600px and up) */
@media only screen and (min-width: 600px) {
.block {
width: calc(96vw / 2);
width: calc(96vw / min(2, var(--max-visible-cards)));
}
}

/* Medium devices (landscape tablets, 768px and up) */
@media only screen and (min-width: 768px) {
.block {
width: calc(96vw / 3);
width: calc(96vw / min(3, var(--max-visible-cards)));
}
.searchBarInput {
width: 200px;
Expand All @@ -1064,7 +1067,7 @@ Producthunt item
/* Large devices (desktops, 992px and up)*/
@media only screen and (min-width: 992px) {
.block {
width: calc(96vw / 3);
width: calc(96vw / min(3, var(--max-visible-cards)));
}

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

/* XX-Large devices (larger desktops, 1400px and up)*/
@media only screen and (min-width: 1400px) {
.block {
width: calc(96vw / 4);
width: calc(95vw / min(4, var(--max-visible-cards)));
}
}

Expand All @@ -1093,7 +1096,9 @@ Producthunt item
padding: 0;
}
.block {
width: calc((1800px - 12px * 4) / 4);
width: calc(
(1800px - 14px * min(5, var(--max-visible-cards))) / min(5, var(--max-visible-cards))
);
}
}

Expand All @@ -1120,3 +1125,6 @@ Producthunt item
position: relative;
vertical-align: middle;
}
.noMargin {
margin: 0 !important;
}
9 changes: 6 additions & 3 deletions src/assets/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
src: url('fonts/nunito/nunito-semibold.woff2') format('woff2');
}

html {
--max-visible-cards: 4;
}

html.dark {
--app-name-text-color: #fff;
--background-color: #0d1116;
Expand Down Expand Up @@ -89,7 +93,7 @@ html.dark {
--settings-input-border-color: #3a4553;
--settings-input-border-focus-color: #6b7b90;
--settings-input-placeholder-color: #42474e;
--settings-input-text-color: #fff
--settings-input-text-color: #fff;
}

html.light {
Expand Down Expand Up @@ -171,6 +175,5 @@ html.light {
--settings-input-border-color: #e9ebec;
--settings-input-border-focus-color: #c4d6df;
--settings-input-placeholder-color: #97a6ad;
--settings-input-text-color: #253b53

--settings-input-text-color: #253b53;
}
6 changes: 4 additions & 2 deletions src/components/Elements/ChipsSet/ChipsSet.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from 'clsx'
import { useState } from 'react'
import { Option } from 'src/types'
import './chipset.css'

type ChipProps = {
option: Option
onSelect: (option: Option) => void
Expand All @@ -19,12 +19,14 @@ const Chip = ({ option, onSelect, active = false }: ChipProps) => {
type ChangeAction = 'ADD' | 'REMOVE'
type ChipsSetProps = {
options: Option[]
className?: string
defaultValues?: string[]
canSelectMultiple?: boolean
onChange?: (action: ChangeAction, options: Option[]) => void
}

export const ChipsSet = ({
className,
options,
canSelectMultiple = false,
onChange,
Expand Down Expand Up @@ -62,7 +64,7 @@ export const ChipsSet = ({
}

return (
<div className="chipsSet">
<div className={clsx('chipsSet', className)}>
{options.map((option) => {
return (
<Chip
Expand Down
8 changes: 8 additions & 0 deletions src/components/Elements/ChipsSet/chipset.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,11 @@
background-color: var(--chip-active-background);
color: var(--chip-active-text);
}

.chipsSet.alternative-color .chip {
background-color: var(--card-action-button-background);
}
.chipsSet.alternative-color .chip.active {
background-color: var(--card-active-action-button-background);
color: var(--card-active-action-button-color);
}
2 changes: 1 addition & 1 deletion src/components/Layout/ScrollCardsNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const ScrollCardsNavigator = () => {
}, [])

useLayoutEffect(() => {
setLeftButtonVisible(false)
scrollBarContainer.current = document.querySelector('.Cards')
}, [])

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

useEffect(() => {
setLeftButtonVisible(false)
setRightButtonVisible(cards.length > maxCardsPerRow)
}, [cards])

Expand Down
53 changes: 52 additions & 1 deletion src/features/settings/components/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ReactModal from 'react-modal'
import Select, { ActionMeta, MultiValue, SingleValue } from 'react-select'
import Toggle from 'react-toggle'
import 'react-toggle/style.css'
import { ChipsSet } from 'src/components/Elements'
import { Footer } from 'src/components/Layout'
import { SUPPORTED_CARDS, SUPPORTED_SEARCH_ENGINES, supportLink } from 'src/config'
import { Tag, useRemoteConfigStore } from 'src/features/remoteConfig'
Expand All @@ -12,19 +13,21 @@ import {
identifyUserLanguages,
identifyUserLinksInNewTab,
identifyUserListingMode,
identifyUserMaxVisibleCards,
identifyUserSearchEngine,
identifyUserTheme,
trackLanguageAdd,
trackLanguageRemove,
trackListingModeSelect,
trackMaxVisibleCardsChange,
trackSearchEngineSelect,
trackSourceAdd,
trackSourceRemove,
trackTabTarget,
trackThemeSelect,
} from 'src/lib/analytics'
import { useUserPreferences } from 'src/stores/preferences'
import { SearchEngineType, SelectedCard } from 'src/types'
import { Option, SearchEngineType, SelectedCard } from 'src/types'
import { RssSetting } from './RssSetting'
import './settings.css'

Expand All @@ -48,8 +51,10 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
listingMode,
theme,
searchEngine,
maxVisibleCards,
setTheme,
setListingMode,
setMaxVisibleCards,
setSearchEngine,
setOpenLinksNewTab,
setCards,
Expand Down Expand Up @@ -144,6 +149,15 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr
identifyUserTheme(newTheme)
}

const onMaxVisibleCardsChange = (selectedChips: Option[]) => {
if (selectedChips.length) {
const maxVisibleCards = parseInt(selectedChips[0].value)
setMaxVisibleCards(maxVisibleCards)
identifyUserMaxVisibleCards(maxVisibleCards)
trackMaxVisibleCardsChange(maxVisibleCards)
}
}

return (
<ReactModal
isOpen={showSettings}
Expand Down Expand Up @@ -215,6 +229,43 @@ export const SettingsModal = ({ showSettings, setShowSettings }: SettingsModalPr

<RssSetting setSelectedCards={setSelectedCards} />

<div className="settingRow">
<p className="settingTitle">Max number of cards to display</p>
<div className="settingContent">
<ChipsSet
className={'noMargin alternative-color'}
canSelectMultiple={false}
options={[
{
label: '3 cards',
value: '3',
},
{
label: '4 cards',
value: '4',
},
{
label: '5 cards',
value: '5',
},
{
label: '6 cards',
value: '6',
},
]}
defaultValues={[maxVisibleCards.toString()]}
onChange={(_, selectedChips) => {
onMaxVisibleCardsChange(selectedChips)
}}
/>

<p className="settingHint">
To ensure a seamless experience, we may adjust the selected number to align with the
resolution of your screen.
</p>
</div>
</div>

<div className="settingRow">
<p className="settingTitle">Dark Mode</p>
<div className="settingContent">
Expand Down
20 changes: 17 additions & 3 deletions src/lib/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum Verbs {
FINISH = 'Finish',
SKIP = 'Skip',
DRAG = 'Drag',
Change = 'Change',
}

export enum Attributes {
Expand All @@ -60,6 +61,7 @@ export enum Attributes {
SOURCE_TAGS = 'Source Tags',
CAMPAIGN_ID = 'Campaign Id',
OCCUPATION = 'Occupation',
MAX_VISIBLE_CARDS = 'Max Visible Cards',
}

const _SEP_ = ' '
Expand All @@ -82,6 +84,7 @@ export const setupIdentification = () => {
listingMode,
openLinksNewTab,
searchEngine,
maxVisibleCards
} = useUserPreferences.getState()

identifyUserProperty(Attributes.RESOLUTION, getScreenResolution())
Expand All @@ -91,6 +94,7 @@ export const setupIdentification = () => {
identifyUserListingMode(listingMode)
identifyUserSearchEngine(searchEngine)
identifyUserLinksInNewTab(openLinksNewTab)
identifyUserMaxVisibleCards(maxVisibleCards)
if (onboardingResult?.title) {
identifyUserOccupation(onboardingResult.title)
}
Expand Down Expand Up @@ -291,6 +295,14 @@ export const trackPageDrag = () => {
})
}

export const trackMaxVisibleCardsChange = (maxVisibleCards: number) => {
trackEvent({
object: Objects.CARD,
verb: Verbs.Change,
attributes: {[Attributes.MAX_VISIBLE_CARDS]: maxVisibleCards}
})
}

// Identification

export const identifyUserLanguages = (languages: string[]) => {
Expand All @@ -317,7 +329,9 @@ export const identifyUserLinksInNewTab = (enabled: boolean) => {
export const identifyUserOccupation = (occupation: string) => {
identifyUserProperty(Attributes.OCCUPATION, occupation)
}

export const identifyUserMaxVisibleCards = (maxVisibleCards: number) => {
identifyUserProperty(Attributes.MAX_VISIBLE_CARDS, maxVisibleCards)
}

// Private functions
type trackEventProps = {
Expand Down Expand Up @@ -365,13 +379,13 @@ const trackEvent = ({ object, verb, attributes }: trackEventProps) => {
}
}

const identifyUserProperty = (attributes: Attributes, value: string | string[]) => {
const identifyUserProperty = (attributes: Attributes, value: string | number | string[]) => {
try {
let formatedValue
if (Array.isArray(value)) {
formatedValue = value.filter(Boolean).map((item) => item.toLowerCase())
} else {
formatedValue = value.toLowerCase()
formatedValue = typeof value === "string" ? value.toLowerCase() : value.toString()
}

if (isDevelopment()) {
Expand Down
4 changes: 4 additions & 0 deletions src/stores/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type UserPreferencesState = {
onboardingResult: Omit<Occupation, 'icon'> | null
listingMode: ListingMode
searchEngine: string
maxVisibleCards: number
cards: SelectedCard[]
cardsSettings: Record<string, CardSettingsType>
firstSeenDate: number
Expand All @@ -28,6 +29,7 @@ type UserPreferencesStoreActions = {
setListingMode: (listingMode: ListingMode) => void
setCards: (selectedCards: SelectedCard[]) => void
setTags: (selectedTags: Tag[]) => void
setMaxVisibleCards: (maxVisibleCards: number) => void
setCardSettings: (card: string, settings: CardSettingsType) => void
markOnboardingAsCompleted: (occupation: Omit<Occupation, 'icon'> | null) => void
setUserCustomCards: (cards: SupportedCardType[]) => void
Expand All @@ -39,6 +41,7 @@ export const useUserPreferences = create(
(set) => ({
userSelectedTags: [],
cardsSettings: {},
maxVisibleCards: 4,
theme: 'dark',
onboardingCompleted: false,
onboardingResult: null,
Expand All @@ -59,6 +62,7 @@ export const useUserPreferences = create(
setOpenLinksNewTab: (openLinksNewTab: boolean) => set({ openLinksNewTab: openLinksNewTab }),
setCards: (selectedCards: SelectedCard[]) => set({ cards: selectedCards }),
setTags: (selectedTags: Tag[]) => set({ userSelectedTags: selectedTags }),
setMaxVisibleCards: (maxVisibleCards: number) => set({ maxVisibleCards: maxVisibleCards }),
initState: (newState: UserPreferencesState) =>
set(() => {
return { ...newState }
Expand Down