Skip to content

Commit 8c1b3e9

Browse files
committed
improve the import/export bookmarks UI
1 parent 61b17e5 commit 8c1b3e9

5 files changed

Lines changed: 76 additions & 52 deletions

File tree

src/components/Layout/SettingsContentLayout/SettingsContentLayout.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ type SettingsContentLayoutProps = {
44
title: string
55
description: string
66
children: React.ReactNode
7+
actions?: React.ReactNode
78
}
89

910
export const SettingsContentLayout = ({
1011
title,
1112
description,
13+
actions,
1214
children,
1315
}: SettingsContentLayoutProps) => {
1416
return (
1517
<div className="settingsContent">
1618
<header>
17-
<h1 className="title">{title}</h1>
18-
<p className="description">{description}</p>
19+
<div className="settingsHeader">
20+
<h1 className="title">{title}</h1>
21+
<p className="description">{description}</p>
22+
</div>
23+
24+
{actions && <div className="actions">{actions}</div>}
1925
</header>
2026
<main className="settingsBody scrollable">{children}</main>
2127
</div>

src/components/Layout/SettingsContentLayout/settingsContentLayout.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,33 @@
1515
gap: 16px;
1616
}
1717
.settingsContent header {
18+
display: flex;
19+
justify-content: space-between;
20+
align-items: center;
21+
}
22+
23+
.settingsContent .settingsHeader {
1824
display: flex;
1925
flex-direction: column;
2026
row-gap: 8px;
2127
}
22-
.settingsContent header .title {
28+
.settingsContent .settingsHeader .title {
2329
font-size: 24px;
2430
margin: 0;
2531
padding: 0;
2632
color: var(--primary-text-color);
2733
}
28-
.settingsContent header .description {
34+
.settingsContent .settingsHeader .description {
2935
font-size: 14px;
3036
margin: 0;
3137
padding: 0;
3238
color: var(--secondary-text-color);
3339
}
34-
40+
.settingsContent .actions {
41+
display: flex;
42+
justify-content: space-between;
43+
align-items: center;
44+
}
3545
@media only screen and (max-width: 768px) {
3646
.settingsContent {
3747
padding: 16px;

src/features/settings/components/BookmarkSettings/BookmarkSettings.tsx

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useRef, useState } from 'react'
1+
import { useRef } from 'react'
22
import { BiBookmarkMinus } from 'react-icons/bi'
3+
import { GrDocumentDownload, GrDocumentUpload } from 'react-icons/gr'
34
import toast from 'react-simple-toasts'
45
import { CardLink } from 'src/components/Elements'
56
import { SettingsContentLayout } from 'src/components/Layout/SettingsContentLayout'
@@ -62,7 +63,7 @@ const BookmarkItem = ({ item, appendRef = false }: BookmarkItemProps) => {
6263

6364
export const BookmarkSettings = () => {
6465
const inputFile = useRef<HTMLInputElement | null>(null)
65-
const {initState, userBookmarks } = useBookmarks()
66+
const { initState, userBookmarks } = useBookmarks()
6667

6768
const importBookmarks = () => {
6869
inputFile.current?.click()
@@ -72,63 +73,66 @@ export const BookmarkSettings = () => {
7273
const blob = new Blob([JSON.stringify(userBookmarks, null, 2)], {
7374
type: 'application/json',
7475
})
75-
const downloadURL = URL.createObjectURL(blob);
76-
const link = document.createElement("a");
77-
link.href = downloadURL;
78-
link.download = "hackertabBookmarks";
79-
link.click();
80-
toast('Your bookmarks have been successfully exported', { theme: 'defaultToast' })
76+
const downloadURL = URL.createObjectURL(blob)
77+
const link = document.createElement('a')
78+
link.href = downloadURL
79+
link.download = 'hackertabBookmarks'
80+
link.click()
8181
}
82+
8283
const handleFileChange = (event: any) => {
83-
const file = event.target.files?.[0];
84+
const file = event.target.files?.[0]
8485
if (file) {
8586
const reader = new FileReader()
8687
reader.onload = () => {
87-
const importData: BookmarkedPost[] = JSON.parse(reader.result as string);
88-
const validatedData = importData.filter(
89-
(data: BookmarkedPost) =>
90-
data.title &&
91-
data.url &&
92-
!userBookmarks.some((bm) => bm.title === data.title && bm.url === data.url))
93-
.map((data) => ({
88+
const importData: BookmarkedPost[] = JSON.parse(reader.result as string)
89+
const validatedData = importData
90+
.filter(
91+
(data: BookmarkedPost) =>
92+
data.title &&
93+
data.url &&
94+
!userBookmarks.some((bm) => bm.title === data.title && bm.url === data.url)
95+
)
96+
.map((data) => ({
9497
title: data.title,
9598
url: data.url,
9699
source: data.source || '',
97100
sourceType: data.sourceType || '',
98-
}));
101+
}))
99102
initState({
100103
userBookmarks: [...userBookmarks, ...validatedData],
101-
});
104+
})
102105
toast('Your bookmarks have been successfully imported', { theme: 'defaultToast' })
103106
}
104107
reader.readAsText(file)
105108
}
106109
}
107110

108-
109111
return (
110112
<>
111-
<div className="btn-group">
112-
<div>
113-
<input
114-
type="file"
115-
id="file"
116-
ref={inputFile}
117-
accept="application/json"
118-
style={{ display: 'none' }}
119-
onChange={handleFileChange}
120-
/>
121-
<button className="notbtn btn" onClick={() => importBookmarks()}>
122-
Import
123-
</button>
124-
</div>
125-
<button className="notbtn btn" onClick={() => exportBookmarks()}>
126-
Export
127-
</button>
128-
</div>
129113
<SettingsContentLayout
130114
title="Bookmarks"
131-
description="Find all your bookmarks here. You can remove a bookmark by clicking on the remove icon.">
115+
description="Find all your bookmarks here. You can remove a bookmark by clicking on the remove icon."
116+
actions={
117+
<>
118+
<input
119+
type="file"
120+
id="file"
121+
ref={inputFile}
122+
accept="application/json"
123+
className="hidden"
124+
onChange={handleFileChange}
125+
/>
126+
<button className="extraBtn extraTextBtn" onClick={() => importBookmarks()}>
127+
<GrDocumentUpload />
128+
&nbsp;Import
129+
</button>
130+
<button className="extraBtn extraTextBtn" onClick={() => exportBookmarks()}>
131+
<GrDocumentDownload />
132+
&nbsp;Export
133+
</button>
134+
</>
135+
}>
132136
<div className="bookmarks">
133137
{userBookmarks.map((bm) => (
134138
<BookmarkItem item={bm} key={bm.url} />

src/features/settings/components/SearchEngineSettings.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ export const SearchEngineSettings = () => {
4949
<hr />
5050
<>
5151
<header>
52-
<h3 className="title">Add new Search Engine</h3>
53-
<p className="description">
54-
Can't find your favorite search engine? Add it here and it.
55-
</p>
52+
<div className="settingsHeader">
53+
<h3 className="title">Add new Search Engine</h3>
54+
<p className="description">
55+
Can't find your favorite search engine? Add it here and it.
56+
</p>
57+
</div>
5658
</header>
5759
<AddSearchEngine />
5860
</>

src/features/settings/components/SourceSettings.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ export const SourceSettings = () => {
6969
<hr />
7070
<>
7171
<header>
72-
<h3 className="title">Add new Source</h3>
73-
<p className="description">
74-
Can't find your favorite source? Add its RSS feed URL here and it will be available in
75-
your feed.
76-
</p>
72+
<div className="settingsHeader">
73+
<h3 className="title">Add new Source</h3>
74+
<p className="description">
75+
Can't find your favorite source? Add its RSS feed URL here and it will be available
76+
in your feed.
77+
</p>
78+
</div>
7779
</header>
7880
<RssSetting />
7981
</>

0 commit comments

Comments
 (0)