Skip to content

Commit 575f10e

Browse files
committed
implement search bar feature
1 parent b8ea34b commit 575f10e

7 files changed

Lines changed: 167 additions & 53 deletions

File tree

src/App.css

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ a {
8989
.extras {
9090

9191
flex-direction: row;
92-
margin-left: auto;
9392
margin-right: 16px;
9493
align-content: center;
9594

@@ -403,7 +402,6 @@ a {
403402
align-self: flex-start;
404403
display: flex;
405404
flex-direction: row;
406-
margin-top: 6px;
407405
flex-wrap: wrap;
408406
gap: 4px;
409407
}
@@ -705,6 +703,26 @@ Producthunt item
705703
background-color: var(--tooltip-accent-color);
706704
}
707705

706+
.searchBar {
707+
position: relative;
708+
margin: 0 auto;
709+
margin-top: 6px;
710+
}
711+
.searchBarIcon {
712+
position: absolute;
713+
height: 46px;
714+
margin: 0 16px;
715+
}
716+
.searchBarInput {
717+
border-radius: 50px;
718+
border:1px solid var(--card-border-color);
719+
box-shadow: 0 0 20px var(--card-border-color);
720+
height: 42px;
721+
padding:0 32px 0 40px;
722+
width:600px;
723+
background-color: var(--card-header-background-color);
724+
}
725+
708726
.tooltipLoading {
709727
display: flex;
710728
justify-content: center;
@@ -770,9 +788,15 @@ Producthunt item
770788
width: 0px;
771789
background: transparent;
772790
}
791+
.searchBar {
792+
display: none;
793+
}
773794
.slogan {
774795
display: none;
775796
}
797+
.tags {
798+
margin-top: 6px;
799+
}
776800
.changelogButton {
777801
display: none;
778802
}

src/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function App() {
3737
theme: getOSMode(),
3838
openLinksNewTab: true,
3939
listingMode: 'normal',
40+
searchEngine: 'Google',
4041
cards: [
4142
{ id: 0, name: 'github' },
4243
{ id: 1, name: 'hackernews' },

src/Constants.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,32 @@ export const SUPPORTED_CARDS = [
109109
},
110110
]
111111

112+
export const SUPPORTED_SEARCH_ENGINES = [
113+
{
114+
label: 'Google',
115+
url: 'https://google.com/search?q=',
116+
},
117+
{
118+
label: 'DuckDuckGo',
119+
url: 'https://duckduckgo.com?q=',
120+
},
121+
{
122+
label: 'Bing',
123+
url: 'https://bing.com/search?q=',
124+
},
125+
{
126+
label: 'Yahoo',
127+
url: 'https://search.yahoo.com/search?p=',
128+
},
129+
{
130+
label: 'Baidu',
131+
url: 'https://baidu.com/s?wd=',
132+
},
133+
{
134+
label: 'Yandex',
135+
url: 'https://yandex.ru/search/?text=',
136+
},
137+
]
112138
export const LS_PREFERENCES_KEY = 'hackerTabPrefs'
113139
export const LS_ANALYTICS_ID_KEY = 'hackerTabAnalyticsId'
114140

src/components/Header.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,43 @@ import { CgTab } from 'react-icons/cg';
44
import { BsFillBookmarksFill } from "react-icons/bs"
55
import { ReactComponent as HackertabLogo } from '../logo.svg';
66
import UserTags from "./UserTags";
7-
import { APP } from '../Constants';
8-
import SettingsModal from "../settings/SettingsModal";
7+
import { SUPPORTED_SEARCH_ENGINES } from '../Constants'
8+
import SettingsModal from '../settings/SettingsModal'
99
import { BsMoon } from 'react-icons/bs'
1010
import { IoMdSunny } from 'react-icons/io'
11-
import { trackThemeChange } from '../utils/Analytics'
11+
import { trackThemeChange, trackSearch } from '../utils/Analytics'
1212
import Changelog from './Changelog'
13+
import { GoSearch } from 'react-icons/go'
1314

15+
function SearchBar({ state }) {
16+
const keywordsInputRef = React.useRef(null)
17+
const userSearchEngine = SUPPORTED_SEARCH_ENGINES.find(
18+
(engine) => engine.label == state.searchEngine
19+
)
20+
21+
const handleSubmit = (e) => {
22+
e.preventDefault()
23+
const keywords = e.target.children[1].value
24+
trackSearch(userSearchEngine.label)
25+
window.open(`${userSearchEngine.url}${keywords}`, '_self')
26+
}
27+
28+
useEffect(() => {
29+
keywordsInputRef.current.focus()
30+
}, [])
31+
32+
return (
33+
<form className="searchBar" onSubmit={handleSubmit}>
34+
<GoSearch className="searchBarIcon" size={20} />
35+
<input
36+
ref={keywordsInputRef}
37+
type="text"
38+
className="searchBarInput"
39+
placeholder={`Search on ${userSearchEngine.label}`}
40+
/>
41+
</form>
42+
)
43+
}
1444
function Header({ state, dispatcher, showSideBar, setShowSideBar, showSettings, setShowSettings }) {
1545
const [themeIcon, setThemeIcon] = useState(<BsMoon />)
1646
const isFirstRun = useRef(true)
@@ -68,7 +98,7 @@ function Header({ state, dispatcher, showSideBar, setShowSideBar, showSettings,
6898
<HackertabLogo className="logoText" />
6999
<Changelog />
70100
</span>
71-
<div className="slogan">{APP.slogan}</div>
101+
<SearchBar state={state} />
72102
<div className="extras">
73103
<button className="extraBtn" onClick={onSettingsClick}>
74104
<BsFillGearFill />

src/preferences/AppReducer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const AppReducer = (state, action) => {
5151
(bm) => bm.source !== value.source || bm.url !== value.url
5252
)
5353
break
54+
case 'setSearchEngine':
55+
newState = { ...newState, searchEngine: value.label }
56+
break
5457
default:
5558
throw new Error()
5659
}
@@ -63,6 +66,7 @@ const AppReducer = (state, action) => {
6366
listingMode: newState.listingMode,
6467
changelogMeta: newState.changelogMeta,
6568
userBookmarks: newState.userBookmarks,
69+
searchEngine: newState.searchEngine,
6670
}
6771
AppStorage.setItem(LS_PREFERENCES_KEY, storageData)
6872
return newState

src/settings/SettingsModal.js

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@ import '../App.css';
88
import './settings.css';
99
import PreferencesContext from '../preferences/PreferencesContext';
1010
import ConfigurationContext from '../configuration/ConfigurationContext';
11-
import { SUPPORTED_CARDS, APP } from '../Constants'
12-
import { ToastContainer, toast } from 'react-toastify';
13-
import 'react-toastify/dist/ReactToastify.css';
14-
import { trackAddLanguage, trackRemoveLanguage, trackAddCard, trackRemoveCard,
15-
trackOpenLinksNewTab, trackListingModeChange } from "../utils/Analytics"
11+
import { SUPPORTED_CARDS, SUPPORTED_SEARCH_ENGINES, APP } from '../Constants'
12+
import { ToastContainer, toast } from 'react-toastify'
13+
import 'react-toastify/dist/ReactToastify.css'
14+
import {
15+
trackAddLanguage,
16+
trackRemoveLanguage,
17+
trackAddCard,
18+
trackRemoveCard,
19+
trackOpenLinksNewTab,
20+
trackListingModeChange,
21+
} from '../utils/Analytics'
1622

1723
function SettingsModal({ showSettings, setShowSettings }) {
18-
1924
const { supportedTags } = useContext(ConfigurationContext)
2025
const preferences = useContext(PreferencesContext)
21-
const { dispatcher, cards, userSelectedTags, openLinksNewTab, listingMode, theme } = preferences
26+
const { dispatcher, cards, userSelectedTags, openLinksNewTab, listingMode, theme, searchEngine } =
27+
preferences
2228
const [selectedCards, setSelectedCards] = useState(cards)
2329

2430
const handleCloseModal = () => {
@@ -73,6 +79,10 @@ function SettingsModal({ showSettings, setShowSettings }) {
7379
dispatcher({ type: 'setCards', value: newCards })
7480
}
7581

82+
const onSearchEngineSelectChange = (value) => {
83+
dispatcher({ type: 'setSearchEngine', value })
84+
}
85+
7686
const onOpenLinksNewTabChange = (e) => {
7787
const checked = e.target.checked
7888
trackOpenLinksNewTab(checked)
@@ -171,6 +181,27 @@ function SettingsModal({ showSettings, setShowSettings }) {
171181
/>
172182
</div>
173183
</div>
184+
185+
<div className="settingRow">
186+
<p className="settingTitle">Favorite search engine</p>
187+
<div className="settingContent">
188+
<Select
189+
options={SUPPORTED_SEARCH_ENGINES}
190+
value={SUPPORTED_SEARCH_ENGINES.find((e) => e.label == searchEngine)}
191+
isMulti={false}
192+
isClearable={false}
193+
isSearchable={false}
194+
classNamePrefix={'hackertab'}
195+
onChange={onSearchEngineSelectChange}
196+
/>
197+
<p className="settingHint">
198+
Missing a search engine? create an issue{' '}
199+
<a href="#" onClick={(e) => window.open(APP.supportLink, '_blank')}>
200+
here
201+
</a>
202+
</p>
203+
</div>
204+
</div>
174205
</div>
175206
<ToastContainer />
176207
</ReactModal>

0 commit comments

Comments
 (0)