Skip to content

Commit 63ab62c

Browse files
committed
feat: retry button
1 parent cc3fcaa commit 63ab62c

File tree

11 files changed

+80
-40
lines changed

11 files changed

+80
-40
lines changed

src/_locales/en/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,6 @@
8484
"Loading...": "Loading...",
8585
"Feedback": "Feedback",
8686
"Confirm": "Confirm",
87-
"Clear Conversation": "Clear Conversation"
87+
"Clear Conversation": "Clear Conversation",
88+
"Retry": "Retry"
8889
}

src/_locales/zh-hans/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,6 @@
8484
"Loading...": "正在读取...",
8585
"Feedback": "反馈",
8686
"Confirm": "确认",
87-
"Clear Conversation": "清理对话"
87+
"Clear Conversation": "清理对话",
88+
"Retry": "重试"
8889
}

src/_locales/zh-hant/main.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,6 @@
8484
"Loading...": "正在讀取...",
8585
"Feedback": "反饋",
8686
"Confirm": "確認",
87-
"Clear Conversation": "清理對話"
87+
"Clear Conversation": "清理對話",
88+
"Retry": "重試"
8889
}

src/background/apis/bing-web.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import BingAIClient from '../clients/BingAIClient'
22
import { getUserConfig } from '../../config/index.mjs'
3+
import { pushRecord } from './shared.mjs'
34

45
/**
56
* @param {Runtime.Port} port
@@ -63,7 +64,7 @@ export async function generateAnswersWithBingWebApi(
6364
session.bingWeb.clientId = response.clientId
6465
session.bingWeb.invocationId = response.invocationId
6566

66-
session.conversationRecords.push({ question: question, answer: answer })
67+
pushRecord(session, question, answer)
6768
console.debug('conversation history', { content: session.conversationRecords })
6869
port.onMessage.removeListener(stopListener)
6970
port.postMessage({ answer: answer, done: true, session: session })

src/background/apis/chatgpt-web.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { fetchSSE } from '../../utils/fetch-sse'
44
import { isEmpty } from 'lodash-es'
55
import { chatgptWebModelKeys, getUserConfig, Models } from '../../config/index.mjs'
6+
import { pushRecord } from './shared.mjs'
67

78
async function request(token, method, path, data) {
89
const apiUrl = (await getUserConfig()).customChatGptWebApiUrl
@@ -105,7 +106,7 @@ export async function generateAnswersWithChatgptWebApi(port, question, session,
105106
onMessage(message) {
106107
console.debug('sse message', message)
107108
if (message === '[DONE]') {
108-
session.conversationRecords.push({ question: question, answer: answer })
109+
pushRecord(session, question, answer)
109110
console.debug('conversation history', { content: session.conversationRecords })
110111
port.postMessage({ answer: null, done: true, session: session })
111112
return

src/background/apis/custom-api.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getUserConfig, maxResponseTokenLength } from '../../config/index.mjs'
99
import { fetchSSE } from '../../utils/fetch-sse'
1010
import { getConversationPairs } from '../../utils/get-conversation-pairs'
1111
import { isEmpty } from 'lodash-es'
12+
import { pushRecord } from './shared.mjs'
1213

1314
const getCustomApiPromptBase = async () => {
1415
return `I am a helpful, creative, clever, and very friendly assistant. I am familiar with various languages in the world.`
@@ -59,7 +60,7 @@ export async function generateAnswersWithCustomApi(port, question, session, apiK
5960
onMessage(message) {
6061
console.debug('sse message', message)
6162
if (message === '[DONE]') {
62-
session.conversationRecords.push({ question: question, answer: answer })
63+
pushRecord(session, question, answer)
6364
console.debug('conversation history', { content: session.conversationRecords })
6465
port.postMessage({ answer: null, done: true, session: session })
6566
return

src/background/apis/openai-api.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { maxResponseTokenLength, Models, getUserConfig } from '../../config/inde
44
import { fetchSSE } from '../../utils/fetch-sse'
55
import { getConversationPairs } from '../../utils/get-conversation-pairs'
66
import { isEmpty } from 'lodash-es'
7+
import { pushRecord } from './shared.mjs'
78

89
const getChatgptPromptBase = async () => {
910
return `You are a helpful, creative, clever, and very friendly assistant. You are familiar with various languages in the world.`
@@ -72,7 +73,7 @@ export async function generateAnswersWithGptCompletionApi(
7273
onMessage(message) {
7374
console.debug('sse message', message)
7475
if (message === '[DONE]') {
75-
session.conversationRecords.push({ question: question, answer: answer })
76+
pushRecord(session, question, answer)
7677
console.debug('conversation history', { content: session.conversationRecords })
7778
port.postMessage({ answer: null, done: true, session: session })
7879
return
@@ -148,7 +149,7 @@ export async function generateAnswersWithChatgptApi(port, question, session, api
148149
onMessage(message) {
149150
console.debug('sse message', message)
150151
if (message === '[DONE]') {
151-
session.conversationRecords.push({ question: question, answer: answer })
152+
pushRecord(session, question, answer)
152153
console.debug('conversation history', { content: session.conversationRecords })
153154
port.postMessage({ answer: null, done: true, session: session })
154155
return

src/background/apis/shared.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function pushRecord(session, question, answer) {
2+
const recordLength = session.conversationRecords.length
3+
let lastRecord
4+
if (recordLength > 0) lastRecord = session.conversationRecords[recordLength - 1]
5+
6+
if (session.isRetry && lastRecord && lastRecord.question === question) lastRecord.answer = answer
7+
else session.conversationRecords.push({ question: question, answer: answer })
8+
}

src/components/ConversationCard/index.jsx

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Browser from 'webextension-polyfill'
44
import InputBox from '../InputBox'
55
import ConversationItem from '../ConversationItem'
66
import { createElementAtPosition, initSession, isSafari } from '../../utils'
7-
import { DownloadIcon } from '@primer/octicons-react'
7+
import { DownloadIcon, LinkExternalIcon } from '@primer/octicons-react'
88
import { WindowDesktop, XLg, Pin } from 'react-bootstrap-icons'
99
import FileSaver from 'file-saver'
1010
import { render } from 'preact'
@@ -94,7 +94,7 @@ function ConversationCard(props) {
9494
* @param {'question'|'answer'|'error'} newType
9595
* @param {boolean} done
9696
*/
97-
const UpdateAnswer = (value, appended, newType, done = false) => {
97+
const updateAnswer = (value, appended, newType, done = false) => {
9898
setConversationItemData((old) => {
9999
const copy = [...old]
100100
const index = copy.findLastIndex((v) => v.type === 'answer')
@@ -121,19 +121,20 @@ function ConversationCard(props) {
121121
useEffect(() => {
122122
const listener = (msg) => {
123123
if (msg.answer) {
124-
UpdateAnswer(msg.answer, false, 'answer')
124+
updateAnswer(msg.answer, false, 'answer')
125125
}
126126
if (msg.session) {
127+
if (msg.done) msg.session = { ...msg.session, isRetry: false }
127128
setSession(msg.session)
128129
}
129130
if (msg.done) {
130-
UpdateAnswer('\n<hr/>', true, 'answer', true)
131+
updateAnswer('\n<hr/>', true, 'answer', true)
131132
setIsReady(true)
132133
}
133134
if (msg.error) {
134135
switch (msg.error) {
135136
case 'UNAUTHORIZED':
136-
UpdateAnswer(
137+
updateAnswer(
137138
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
138139
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
139140
}<br>${t('And refresh this page or type you question again')}` +
@@ -145,7 +146,7 @@ function ConversationCard(props) {
145146
)
146147
break
147148
case 'CLOUDFLARE':
148-
UpdateAnswer(
149+
updateAnswer(
149150
`${t('OpenAI Security Check Required')}<br>${
150151
isSafari()
151152
? t('Please open https://chat.openai.com/api/auth/session')
@@ -159,10 +160,7 @@ function ConversationCard(props) {
159160
)
160161
break
161162
default:
162-
setConversationItemData([
163-
...conversationItemData,
164-
new ConversationItemData('error', msg.error + '\n<hr/>'),
165-
])
163+
updateAnswer(msg.error + '\n<hr/>', false, 'error')
166164
break
167165
}
168166
setIsReady(true)
@@ -223,6 +221,18 @@ function ConversationCard(props) {
223221
/>
224222
)}
225223
<span className="gpt-util-group">
224+
{session && session.conversationId && (
225+
<a
226+
title={t('Continue on official website')}
227+
href={'https://chat.openai.com/chat/' + session.conversationId}
228+
target="_blank"
229+
rel="nofollow noopener noreferrer"
230+
className="gpt-util-icon"
231+
style="color: inherit;"
232+
>
233+
<LinkExternalIcon size={16} />
234+
</a>
235+
)}
226236
<DeleteButton
227237
size={16}
228238
onConfirm={() => {
@@ -269,6 +279,27 @@ function ConversationCard(props) {
269279
session={session}
270280
done={data.done}
271281
port={port}
282+
onRetry={
283+
idx === conversationItemData.length - 1
284+
? () => {
285+
updateAnswer(
286+
`<p class="gpt-loading">${t('Waiting for response...')}</p>`,
287+
false,
288+
'answer',
289+
)
290+
setIsReady(false)
291+
292+
const newSession = { ...session, isRetry: true }
293+
setSession(newSession)
294+
try {
295+
port.postMessage({ stop: true })
296+
port.postMessage({ session: newSession })
297+
} catch (e) {
298+
updateAnswer(e, false, 'error')
299+
}
300+
}
301+
: null
302+
}
272303
/>
273304
))}
274305
</div>
@@ -283,12 +314,12 @@ function ConversationCard(props) {
283314
setConversationItemData([...conversationItemData, newQuestion, newAnswer])
284315
setIsReady(false)
285316

286-
const newSession = { ...session, question }
317+
const newSession = { ...session, question, isRetry: false }
287318
setSession(newSession)
288319
try {
289320
port.postMessage({ session: newSession })
290321
} catch (e) {
291-
UpdateAnswer(e, false, 'error')
322+
updateAnswer(e, false, 'error')
292323
}
293324
}}
294325
/>

src/components/ConversationItem/index.jsx

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { useState } from 'react'
2-
import FeedbackForChatGPTWeb from '../FeedbackForChatGPTWeb'
3-
import { ChevronDownIcon, LinkExternalIcon, XCircleIcon } from '@primer/octicons-react'
2+
import { ChevronDownIcon, XCircleIcon, SyncIcon } from '@primer/octicons-react'
43
import CopyButton from '../CopyButton'
54
import PropTypes from 'prop-types'
65
import MarkdownRender from '../MarkdownRender/markdown.jsx'
76
import { useTranslation } from 'react-i18next'
87

9-
export function ConversationItem({ type, content, session, done, port }) {
8+
export function ConversationItem({ type, content, session, done, port, onRetry }) {
109
const { t } = useTranslation()
1110
const [collapsed, setCollapsed] = useState(false)
1211

@@ -59,23 +58,10 @@ export function ConversationItem({ type, content, session, done, port }) {
5958
{t('Stop')}
6059
</button>
6160
)}
62-
{done && session && session.conversationId && (
63-
<FeedbackForChatGPTWeb
64-
messageId={session.messageId}
65-
conversationId={session.conversationId}
66-
/>
67-
)}
68-
{session && session.conversationId && (
69-
<a
70-
title={t('Continue on official website')}
71-
href={'https://chat.openai.com/chat/' + session.conversationId}
72-
target="_blank"
73-
rel="nofollow noopener noreferrer"
74-
className="gpt-util-icon"
75-
style="color: inherit;"
76-
>
77-
<LinkExternalIcon size={14} />
78-
</a>
61+
{onRetry && (
62+
<span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
63+
<SyncIcon size={14} />
64+
</span>
7965
)}
8066
{session && <CopyButton contentFn={() => content} size={14} />}
8167
{!collapsed ? (
@@ -106,6 +92,11 @@ export function ConversationItem({ type, content, session, done, port }) {
10692
<div className="gpt-header">
10793
<p>{t('Error')}:</p>
10894
<div className="gpt-util-group">
95+
{onRetry && (
96+
<span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
97+
<SyncIcon size={14} />
98+
</span>
99+
)}
109100
<CopyButton contentFn={() => content} size={14} />
110101
{!collapsed ? (
111102
<span
@@ -138,6 +129,7 @@ ConversationItem.propTypes = {
138129
session: PropTypes.object.isRequired,
139130
done: PropTypes.bool.isRequired,
140131
port: PropTypes.object.isRequired,
132+
onRetry: PropTypes.func,
141133
}
142134

143135
export default ConversationItem

0 commit comments

Comments
 (0)