Skip to content

Commit a8ea068

Browse files
committed
feat: loading bar
1 parent 9fc266e commit a8ea068

File tree

8 files changed

+139
-39
lines changed

8 files changed

+139
-39
lines changed

src/api/saveChat.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import type { SaveBehavior, StoredDatabase } from "~utils/types"
1515
import type { parseSave } from "./parseSave"
1616

1717
// save new page to notion database
18-
export const saveChat = async (params: SaveChatParams) => {
18+
export const saveChat = async (
19+
params: SaveChatParams,
20+
callback: (saved: number) => void = () => {}
21+
) => {
1922
try {
2023
const notion = await getNotion()
2124
let {
@@ -64,34 +67,36 @@ export const saveChat = async (params: SaveChatParams) => {
6467
url: "https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg"
6568
}
6669
},
67-
properties: tag ? {
68-
[propertiesIds.title]: {
69-
title: [
70-
{
71-
text: {
72-
content: title
73-
}
70+
properties: tag
71+
? {
72+
[propertiesIds.title]: {
73+
title: [
74+
{
75+
text: {
76+
content: title
77+
}
78+
}
79+
]
80+
},
81+
[propertiesIds.url]: {
82+
url
83+
},
84+
[tags[tagPropertyIndex].id]: tag
85+
}
86+
: {
87+
[propertiesIds.title]: {
88+
title: [
89+
{
90+
text: {
91+
content: title
92+
}
93+
}
94+
]
95+
},
96+
[propertiesIds.url]: {
97+
url
7498
}
75-
]
76-
},
77-
[propertiesIds.url]: {
78-
url
79-
},
80-
[tags[tagPropertyIndex].id]: tag
81-
} : {
82-
[propertiesIds.title]: {
83-
title: [
84-
{
85-
text: {
86-
content: title
87-
}
88-
}
89-
]
90-
},
91-
[propertiesIds.url]: {
92-
url
93-
}
94-
},
99+
},
95100
children: generateHeadings
96101
? [table_of_contents, ...chunks[0]]
97102
: [...chunks[0]]

src/background/functions/save.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,25 @@ const save = async (
6464
isMarkdown: true
6565
})
6666

67-
await storage.set(STORAGE_KEYS.saveStatus, "saving" as SaveStatus)
68-
const res = await saveChat({
69-
...parsedReq,
70-
conflictingPageId: conflictingPageId,
71-
generateHeadings,
72-
saveBehavior
73-
})
67+
console.log("Chunks to save:", parsedReq.chunks.length)
68+
69+
await storage.set(
70+
STORAGE_KEYS.saveStatus,
71+
`saving:${0}:${parsedReq.chunks.length}` as SaveStatus
72+
)
73+
const res = await saveChat(
74+
{
75+
...parsedReq,
76+
conflictingPageId: conflictingPageId,
77+
generateHeadings,
78+
saveBehavior
79+
},
80+
(saved) =>
81+
storage.set(
82+
STORAGE_KEYS.saveStatus,
83+
`saving:${saved}:${parsedReq.chunks.length}` as SaveStatus
84+
)
85+
)
7486

7587
await storage.set(STORAGE_KEYS.saveStatus, "saved" as SaveStatus)
7688
if (openInNotion && !autoSave) {

src/contents/popup.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useState } from "react"
55

66
import { useStorage } from "@plasmohq/storage/hook"
77

8+
import useSavePercentage from "~hooks/useSavePercentage"
89
import ErrorPopup from "~popup/ErrorPopup"
910
import SavePopup from "~popup/SavePopup"
1011
import SettingsPopup from "~popup/SettingsPopup"
@@ -36,6 +37,8 @@ const Wrapper = () => {
3637
)
3738
const [toBeSaved, setToBeSaved] = useStorage(STORAGE_KEYS.toBeSaved)
3839

40+
const savePercent = useSavePercentage(saveStatus)
41+
3942
const hidePopup = async () => {
4043
await setShowPopup(false)
4144
await setSaveStatus(null)
@@ -77,7 +80,8 @@ const Wrapper = () => {
7780
<img src={illustration} alt="ChatGPT to Notion" />
7881
<p className="font-semibold">
7982
{saveStatus === "fetching" && "Fetching conversation..."}
80-
{saveStatus === "saving" && "Saving..."}
83+
{saveStatus.includes("saving") &&
84+
"Saving... " + String(savePercent) + "%"}
8185
{saveStatus === "saved" && "Saved successfully!"}
8286
</p>
8387
</div>

src/hooks/useSavePercentage.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useEffect, useMemo, useRef, useState } from "react"
2+
3+
import type { SaveStatus } from "~utils/types"
4+
5+
export default function useSavePercentage(
6+
saveStatus: SaveStatus,
7+
duration: number = 7000
8+
) {
9+
const [start, end] = useMemo(() => {
10+
if (saveStatus && saveStatus.startsWith("saving:")) {
11+
const [_, saved, total] = saveStatus.split(":")
12+
return [
13+
Math.round((parseInt(saved) / parseInt(total)) * 100),
14+
Math.min(Math.round((parseInt(saved + 1) / parseInt(total)) * 100), 90)
15+
]
16+
}
17+
return [0, 0]
18+
}, [saveStatus])
19+
20+
const [percentage, setPercentage] = useState(start)
21+
22+
useEffect(() => {
23+
if (start < end) {
24+
const range = end - start
25+
const increment = range / (duration / 100)
26+
let currentPercentage = start
27+
28+
const intervalId = setInterval(() => {
29+
currentPercentage += increment
30+
if (currentPercentage >= end) {
31+
currentPercentage = end
32+
clearInterval(intervalId)
33+
}
34+
setPercentage((_) => Math.round(currentPercentage))
35+
}, 100)
36+
37+
return () => clearInterval(intervalId)
38+
}
39+
}, [start, end, duration])
40+
41+
return percentage
42+
}

src/popup/IndexPopup.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import NoTagButton from "~common/components/NoTagButton"
2626
import Spinner from "~common/components/Spinner"
2727
import LogoIcon from "~common/logo"
2828
import StarIcon from "~common/star"
29+
import usePercentageAnimation from "~hooks/useSavePercentage"
30+
import useSavePercentage from "~hooks/useSavePercentage"
2931
import useTags from "~hooks/useTags"
3032
import { STORAGE_KEYS } from "~utils/consts"
3133
import {
@@ -76,6 +78,8 @@ function IndexPopup() {
7678
string | undefined
7779
>()
7880

81+
const savePercent = useSavePercentage(saveStatus, 5000)
82+
7983
useEffect(() => {
8084
chrome.runtime
8185
.sendMessage({ type: "chatgpt-to-notion_getCurrentTab" })
@@ -96,7 +100,7 @@ function IndexPopup() {
96100
}, [chatID])
97101

98102
useEffect(() => {
99-
setLoading(saveStatus == "fetching" || saveStatus == "saving")
103+
setLoading(saveStatus == "fetching" || !!saveStatus?.includes("saving"))
100104
}, [saveStatus])
101105

102106
const handleSave = async (autosave?: boolean) => {
@@ -339,6 +343,17 @@ function IndexPopup() {
339343
)}
340344
{loading && <Spinner white small />}
341345
</button>
346+
{loading && (
347+
<div className="flex items-center gap-4">
348+
<div className="flex-grow h-2 rounded-full bg-neutral-300">
349+
<div
350+
style={{ width: String(savePercent) + "%" }}
351+
className="h-full rounded-full bg-black"
352+
/>
353+
</div>
354+
<p className="text-sm font-bold mt-1">{savePercent}%</p>
355+
</div>
356+
)}
342357
{!success && !error && !loading && (
343358
<>
344359
<div className="mt-1">

src/popup/SavePopup.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
Error,
1010
PopupEnum,
1111
SaveBehavior,
12+
SaveStatus,
1213
StoredDatabase,
1314
ToBeSaved
1415
} from "~utils/types"
@@ -20,6 +21,7 @@ import { parseSave } from "~api/parseSave"
2021
import DropdownPopup from "~common/components/Dropdown"
2122
import NoTagButton from "~common/components/NoTagButton"
2223
import Spinner from "~common/components/Spinner"
24+
import useSavePercentage from "~hooks/useSavePercentage"
2325
import useTags from "~hooks/useTags"
2426
import { STORAGE_KEYS } from "~utils/consts"
2527
import { getConsiseErrMessage, i18n } from "~utils/functions"
@@ -72,6 +74,9 @@ export default function SavePopup() {
7274
string | undefined
7375
>()
7476

77+
const [saveStatus] = useStorage<SaveStatus>(STORAGE_KEYS.saveStatus)
78+
const savePercent = useSavePercentage(saveStatus, 3000)
79+
7580
useEffect(() => {
7681
chrome.runtime
7782
.sendMessage({ type: "chatgpt-to-notion_getCurrentTab" })
@@ -375,6 +380,17 @@ export default function SavePopup() {
375380
{i18n("about_FAQ")}
376381
</a>
377382
)}
383+
{loading && (
384+
<div className="flex items-center gap-4">
385+
<div className="flex-grow h-2 rounded-full bg-neutral-300">
386+
<div
387+
style={{ width: String(savePercent) + "%" }}
388+
className="h-full rounded-full bg-black"
389+
/>
390+
</div>
391+
<p className="text-sm font-bold mt-1">{savePercent}%</p>
392+
</div>
393+
)}
378394
</>
379395
)
380396
}

src/utils/functions/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import { markdownToBlocks } from "@tryfabric/martian"
44
import { Storage } from "@plasmohq/storage"
55

66
import nhm from "~config/html-markdown"
7-
import type { ChatConfig, Conversation, Error, Message } from "~utils/types"
7+
import type {
8+
ChatConfig,
9+
Conversation,
10+
Error,
11+
Message,
12+
SaveStatus
13+
} from "~utils/types"
814

915
import { generateCallout, generateToggle } from "./notion"
1016

src/utils/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export type AutosaveStatus =
6464
| "error"
6565
| "disabled"
6666

67-
export type SaveStatus = "saving" | "saved" | "error" | "fetching" | null
67+
export type SaveStatus = `saving:${number}:${number}` | "saved" | "error" | "fetching" | null
6868

6969
// ChatGPT conversation as returned by the API
7070
// We won't type more than we need obviously

0 commit comments

Comments
 (0)