Skip to content

Commit 0d08b1e

Browse files
authored
Merge pull request #107 from medyo/develop
Version 1.15.1
2 parents 8fd5c75 + dba66de commit 0d08b1e

29 files changed

Lines changed: 547 additions & 318 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"axios-cache-adapter": "^2.7.3",
1515
"country-emoji": "^1.5.4",
1616
"dompurify": "^2.2.7",
17+
"eslint-plugin-react": "^7.28.0",
1718
"localforage": "^1.9.0",
1819
"normalize.css": "^8.0.1",
1920
"prop-types": "^15.0.0-0",
@@ -29,14 +30,14 @@
2930
"react-scripts": "4.0.1",
3031
"react-select": "^5.0.1",
3132
"react-spinners": "^0.10.4",
33+
"react-spring-bottom-sheet": "^3.4.1",
3234
"react-toggle": "^4.1.1",
3335
"react-tooltip": "^4.2.21",
3436
"styled-components": "2",
3537
"timeago.js": "^4.0.2",
3638
"type-fest": "^1.2.0",
3739
"web-vitals": "^0.2.4",
38-
"zustand": "^4.1.3",
39-
"eslint-plugin-react": "^7.28.0"
40+
"zustand": "^4.1.3"
4041
},
4142
"proxy": "https://api.hackertab.dev/",
4243
"scripts": {

public/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "Hackertab.dev - developer news",
33
"description": "All developer news in one tab",
4-
"version": "1.14.3",
4+
"version": "1.15.1",
55
"manifest_version": 3,
66
"chrome_url_overrides": {
77
"newtab": "index.html"

src/assets/App.css

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -638,12 +638,6 @@ Producthunt item
638638
position: relative;
639639
}
640640

641-
.marketingBanner {
642-
width: 48%;
643-
margin: 0 auto;
644-
margin-bottom: 16px;
645-
}
646-
647641
.counterWrapper {
648642
display: flex;
649643
flex-direction: column;
@@ -701,7 +695,8 @@ Producthunt item
701695
overflow-x: hidden !important;
702696
}
703697
.changelogButton {
704-
width: 20px;
698+
width: auto;
699+
font-size: 12px;
705700
height: 20px;
706701
background-color: gray;
707702
border-radius: 20px;
@@ -714,13 +709,11 @@ Producthunt item
714709
border: 2px solid var(--background-color);
715710
left: -6px;
716711
top: -10px;
712+
padding:0 4px;
713+
text-transform: lowercase;
717714
color: white;
718715
}
719716
.changelogButton.active {
720-
width: auto;
721-
font-size: 12px;
722-
padding:0 4px;
723-
text-transform: lowercase;
724717
background-color: var(--tooltip-accent-color);
725718
}
726719
.changelogNewButton {
@@ -963,6 +956,34 @@ Producthunt item
963956
margin-right: 16px;
964957
border-radius: 4px;
965958
}
959+
960+
.floatingFilter {
961+
background: rgb(44, 128, 232);
962+
border: transparent;
963+
width: 48px;
964+
height: 48px;
965+
border-radius: 48px;
966+
position: fixed;
967+
bottom: 78px;
968+
z-index: 2;
969+
right: 32px;
970+
display: none;
971+
box-shadow: 0 2px 8px var(--card-actions-background-shadow);
972+
}
973+
.floatingFilterBottomSheet .title {
974+
font-size: 22px;
975+
margin:0 0 8px 0;
976+
}
977+
978+
.floatingFilterIcon {
979+
color: white;
980+
font-size: 24px;
981+
}
982+
@media only screen and (max-width: 600px) {
983+
.floatingFilter {
984+
display: block
985+
}
986+
}
966987
/* Small devices (portrait tablets and large phones, 600px and up) */
967988
@media only screen and (min-width: 600px) {
968989
.block {
@@ -1038,4 +1059,26 @@ Producthunt item
10381059
.Page .buttonIcon {
10391060
position: relative;
10401061
vertical-align: middle;
1062+
}
1063+
1064+
1065+
.chipsSet {
1066+
margin: 6px 0;
1067+
display: flex;
1068+
gap: 12px;
1069+
flex-wrap: wrap;
1070+
}
1071+
1072+
.chip {
1073+
font-weight: 500;
1074+
border-radius: 50px;
1075+
padding:6px 12px;
1076+
border: transparent;
1077+
font-family: "nunito";
1078+
color:var(--chip-text);
1079+
background-color: var(--chip-background);
1080+
}
1081+
.chip.active {
1082+
background-color: var(--chip-active-background);
1083+
color:var(--chip-active-text);
10411084
}

src/assets/variables.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ html.dark {
5959
--tooltip-accent-color: #0366D6;
6060

6161
--placeholder-background-color: #1c2026;
62+
63+
--rsbs-bg: #0D1116;
64+
--chip-background: #0D1116;
65+
--chip-text: #FFF;
66+
--chip-active-background: #576172;
67+
--chip-active-text: #FFF;
6268
}
6369

6470
html.light {
@@ -110,4 +116,11 @@ html.light {
110116

111117
--placeholder-background-color: #e7eff7;
112118

119+
--rsbs-bg: #fff;
120+
121+
--chip-background: #e7eff7;
122+
--chip-text: #3C5065;
123+
--chip-active-background: #0366D6;
124+
--chip-active-text: #FFF;
125+
113126
}

src/components/Elements/Card/Card.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ type CardProps = {
1010
titleComponent?: React.ReactNode
1111
fullBlock?: boolean
1212
}
13-
export const Card = ({
14-
card: { link, icon, label },
15-
titleComponent,
16-
children,
17-
fullBlock = false,
18-
}: CardProps) => {
19-
const { openLinksNewTab } = useUserPreferences()
2013

14+
export const Card = ({ card, titleComponent, children, fullBlock = false }: CardProps) => {
15+
const { openLinksNewTab } = useUserPreferences()
16+
const { link, icon, label } = card
2117
const handleHeaderLinkClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
2218
e.preventDefault()
2319
let url = `${link}?${ref}`
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Option } from 'src/types'
2+
import { useState } from 'react'
3+
4+
type ChipProps = {
5+
option: Option
6+
onSelect: (option: Option) => void
7+
active: boolean
8+
}
9+
10+
const Chip = ({ option, onSelect, active = false }: ChipProps) => {
11+
return (
12+
<button className={'chip ' + (active && 'active')} onClick={() => onSelect(option)}>
13+
{option.label}
14+
</button>
15+
)
16+
}
17+
18+
type ChipsSetProps = {
19+
options: Option[]
20+
defaultValue: Option
21+
onChange: (option: Option) => void
22+
}
23+
24+
export const ChipsSet = ({ options, onChange, defaultValue }: ChipsSetProps) => {
25+
const [selectedChip, setSelectedChip] = useState<Option>(defaultValue)
26+
27+
const onSelect = (option: Option) => {
28+
setSelectedChip(option)
29+
onChange(option)
30+
}
31+
32+
return (
33+
<div className="chipsSet">
34+
{options.map((option) => {
35+
return (
36+
<Chip option={option} onSelect={onSelect} active={selectedChip.value === option.value} />
37+
)
38+
})}
39+
</div>
40+
)
41+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./ChipsSet"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { SupportedCard, GLOBAL_TAG, MY_LANGUAGES_TAG, dateRanges } from 'src/config'
2+
import { FiFilter } from 'react-icons/fi'
3+
import { useState } from 'react'
4+
import { BottomSheet } from 'react-spring-bottom-sheet'
5+
import 'react-spring-bottom-sheet/dist/style.css'
6+
import { useUserPreferences } from 'src/stores/preferences'
7+
import { trackCardLanguageSelect, trackCardDateRangeSelect } from 'src/lib/analytics'
8+
import { ChipsSet } from 'src/components/Elements'
9+
10+
type ListingFilterMobileProps = {
11+
card: SupportedCard
12+
filters?: ('datesRange' | 'language')[]
13+
}
14+
15+
export const FloatingFilter = ({ card, filters = ['language'] }: ListingFilterMobileProps) => {
16+
const [open, setOpen] = useState(false)
17+
const { userSelectedTags, cardsSettings, setCardSettings } = useUserPreferences()
18+
const [availableTagOptions] = useState(
19+
[GLOBAL_TAG, ...userSelectedTags, MY_LANGUAGES_TAG].map((tag) => ({
20+
label: tag.label,
21+
value: tag.value,
22+
}))
23+
)
24+
return (
25+
<>
26+
<button
27+
className="floatingFilter"
28+
onClick={() => setOpen(true)}
29+
style={open ? { display: 'none' } : {}}>
30+
<FiFilter className="floatingFilterIcon" />
31+
</button>
32+
33+
<BottomSheet
34+
defaultSnap={({ maxHeight }) => maxHeight / 2}
35+
open={open}
36+
expandOnContentDrag={true}
37+
onDismiss={() => setOpen(false)}>
38+
<div style={{ padding: '16px' }}>
39+
<div className="settings floatingFilterBottomSheet">
40+
<h1 className="title">Customize {card.label}</h1>
41+
42+
{filters.includes('language') && (
43+
<div className="settingRow">
44+
<p className="settingTitle">Language</p>
45+
<div className="settingContent">
46+
<ChipsSet
47+
defaultValue={
48+
availableTagOptions.find(
49+
(tag) => tag.value === cardsSettings[card.value]?.language
50+
) || {
51+
label: GLOBAL_TAG.label,
52+
value: GLOBAL_TAG.value,
53+
}
54+
}
55+
options={availableTagOptions}
56+
onChange={(option) => {
57+
setCardSettings(card.value, {
58+
...cardsSettings[card.value],
59+
language: option.value,
60+
})
61+
trackCardLanguageSelect(card.analyticsTag, option.value)
62+
}}
63+
/>
64+
</div>
65+
</div>
66+
)}
67+
68+
{filters.includes('datesRange') && (
69+
<div className="settingRow">
70+
<p className="settingTitle">Date Range</p>
71+
<div className="settingContent">
72+
<ChipsSet
73+
defaultValue={
74+
dateRanges.find(
75+
(date) => date.value === cardsSettings[card.value]?.dateRange
76+
) || dateRanges[0]
77+
}
78+
options={dateRanges}
79+
onChange={(option) => {
80+
setCardSettings(card.value, {
81+
...cardsSettings[card.value],
82+
dateRange: option.value,
83+
})
84+
trackCardDateRangeSelect(card.analyticsTag, option.value)
85+
}}
86+
/>
87+
</div>
88+
</div>
89+
)}
90+
</div>
91+
</div>
92+
</BottomSheet>
93+
</>
94+
)
95+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./FloatingFilter"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import DropDownMenu from '../../DropDownMenu'
2+
3+
type Option = {
4+
label: string
5+
value: string
6+
}
7+
type InlineTextFilterProps = {
8+
options: Option[]
9+
value: string | undefined
10+
onChange: (option: Option) => void
11+
}
12+
13+
export const InlineTextFilter = ({ options, value, onChange }: InlineTextFilterProps) => {
14+
const tagId = `inline-text-filter-${Math.random().toString(16).slice(2)}`
15+
return (
16+
<DropDownMenu
17+
data={options}
18+
tagId={tagId}
19+
label={options.find((opt) => opt.value === value)?.label || options[0].label}
20+
setSelectedDropDownItem={(item: Option) => {
21+
onChange(item)
22+
}}
23+
/>
24+
)
25+
}

0 commit comments

Comments
 (0)