@@ -5,24 +5,25 @@ import InputBox from '../InputBox'
5
5
import ConversationItem from '../ConversationItem'
6
6
import { createElementAtPosition , isFirefox , isMobile , isSafari } from '../../utils'
7
7
import {
8
- LinkExternalIcon ,
9
8
ArchiveIcon ,
10
9
DesktopDownloadIcon ,
10
+ LinkExternalIcon ,
11
11
MoveToBottomIcon ,
12
12
} from '@primer/octicons-react'
13
- import { WindowDesktop , XLg , Pin } from 'react-bootstrap-icons'
13
+ import { Pin , WindowDesktop , XLg } from 'react-bootstrap-icons'
14
14
import FileSaver from 'file-saver'
15
15
import { render } from 'preact'
16
16
import FloatingToolbar from '../FloatingToolbar'
17
17
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
18
- import { ModelMode , Models } from '../../config/index.mjs'
18
+ import { bingWebModelKeys , getUserConfig , ModelMode , Models } from '../../config/index.mjs'
19
19
import { useTranslation } from 'react-i18next'
20
20
import DeleteButton from '../DeleteButton'
21
21
import { useConfig } from '../../hooks/use-config.mjs'
22
22
import { createSession } from '../../services/local-session.mjs'
23
23
import { v4 as uuidv4 } from 'uuid'
24
24
import { initSession } from '../../services/init-session.mjs'
25
25
import { findLastIndex } from 'lodash-es'
26
+ import { generateAnswersWithBingWebApi } from '../../services/apis/bing-web.mjs'
26
27
27
28
const logo = Browser . runtime . getURL ( 'logo.png' )
28
29
@@ -48,6 +49,7 @@ function ConversationCard(props) {
48
49
const windowSize = useClampWindowSize ( [ 750 , 1500 ] , [ 250 , 1100 ] )
49
50
const bodyRef = useRef ( null )
50
51
const [ completeDraggable , setCompleteDraggable ] = useState ( false )
52
+ const useForegroundFetch = bingWebModelKeys . includes ( session . modelName )
51
53
52
54
/**
53
55
* @type {[ConversationItemData[], (conversationItemData: ConversationItemData[]) => void] }
@@ -96,12 +98,12 @@ function ConversationCard(props) {
96
98
}
97
99
} , [ conversationItemData ] )
98
100
99
- useEffect ( ( ) => {
101
+ useEffect ( async ( ) => {
100
102
// when the page is responsive, session may accumulate redundant data and needs to be cleared after remounting and before making a new request
101
103
if ( props . question ) {
102
104
const newSession = initSession ( { question : props . question } )
103
105
setSession ( newSession )
104
- port . postMessage ( { session : newSession } )
106
+ await postMessage ( { session : newSession } )
105
107
}
106
108
} , [ props . question ] ) // usually only triggered once
107
109
@@ -125,6 +127,100 @@ function ConversationCard(props) {
125
127
} )
126
128
}
127
129
130
+ const portMessageListener = ( msg ) => {
131
+ if ( msg . answer ) {
132
+ updateAnswer ( msg . answer , false , 'answer' )
133
+ }
134
+ if ( msg . session ) {
135
+ if ( msg . done ) msg . session = { ...msg . session , isRetry : false }
136
+ setSession ( msg . session )
137
+ }
138
+ if ( msg . done ) {
139
+ updateAnswer ( '' , true , 'answer' , true )
140
+ setIsReady ( true )
141
+ }
142
+ if ( msg . error ) {
143
+ switch ( msg . error ) {
144
+ case 'UNAUTHORIZED' :
145
+ updateAnswer (
146
+ `${ t ( 'UNAUTHORIZED' ) } <br>${ t ( 'Please login at https://chat.openai.com first' ) } ${
147
+ isSafari ( ) ? `<br>${ t ( 'Then open https://chat.openai.com/api/auth/session' ) } ` : ''
148
+ } <br>${ t ( 'And refresh this page or type you question again' ) } ` +
149
+ `<br><br>${ t (
150
+ 'Consider creating an api key at https://platform.openai.com/account/api-keys' ,
151
+ ) } `,
152
+ false ,
153
+ 'error' ,
154
+ )
155
+ break
156
+ case 'CLOUDFLARE' :
157
+ updateAnswer (
158
+ `${ t ( 'OpenAI Security Check Required' ) } <br>${
159
+ isSafari ( )
160
+ ? t ( 'Please open https://chat.openai.com/api/auth/session' )
161
+ : t ( 'Please open https://chat.openai.com' )
162
+ } <br>${ t ( 'And refresh this page or type you question again' ) } ` +
163
+ `<br><br>${ t (
164
+ 'Consider creating an api key at https://platform.openai.com/account/api-keys' ,
165
+ ) } `,
166
+ false ,
167
+ 'error' ,
168
+ )
169
+ break
170
+ default :
171
+ if ( conversationItemData [ conversationItemData . length - 1 ] . content . includes ( 'gpt-loading' ) )
172
+ updateAnswer ( msg . error , false , 'error' )
173
+ else
174
+ setConversationItemData ( [
175
+ ...conversationItemData ,
176
+ new ConversationItemData ( 'error' , msg . error ) ,
177
+ ] )
178
+ break
179
+ }
180
+ setIsReady ( true )
181
+ }
182
+ }
183
+
184
+ const foregroundMessageListeners = useRef ( [ ] )
185
+
186
+ /**
187
+ * @param {Session|undefined } session
188
+ * @param {boolean|undefined } stop
189
+ */
190
+ const postMessage = async ( { session, stop } ) => {
191
+ if ( useForegroundFetch ) {
192
+ foregroundMessageListeners . current . forEach ( ( listener ) => listener ( { session, stop } ) )
193
+ if ( session ) {
194
+ const fakePort = {
195
+ postMessage : ( msg ) => {
196
+ portMessageListener ( msg )
197
+ } ,
198
+ onMessage : {
199
+ addListener : ( listener ) => {
200
+ foregroundMessageListeners . current . push ( listener )
201
+ } ,
202
+ removeListener : ( listener ) => {
203
+ foregroundMessageListeners . current . splice (
204
+ foregroundMessageListeners . current . indexOf ( listener ) ,
205
+ 1 ,
206
+ )
207
+ } ,
208
+ } ,
209
+ onDisconnect : {
210
+ addListener : ( ) => { } ,
211
+ removeListener : ( ) => { } ,
212
+ } ,
213
+ }
214
+ const bingToken = ( await getUserConfig ( ) ) . bingAccessToken
215
+ if ( session . modelName . includes ( 'bingFreeSydney' ) )
216
+ await generateAnswersWithBingWebApi ( fakePort , session . question , session , bingToken , true )
217
+ else await generateAnswersWithBingWebApi ( fakePort , session . question , session , bingToken )
218
+ }
219
+ } else {
220
+ port . postMessage ( { session, stop } )
221
+ }
222
+ }
223
+
128
224
useEffect ( ( ) => {
129
225
const portListener = ( ) => {
130
226
setPort ( Browser . runtime . connect ( ) )
@@ -146,68 +242,17 @@ function ConversationCard(props) {
146
242
}
147
243
} , [ port ] )
148
244
useEffect ( ( ) => {
149
- const listener = ( msg ) => {
150
- if ( msg . answer ) {
151
- updateAnswer ( msg . answer , false , 'answer' )
152
- }
153
- if ( msg . session ) {
154
- if ( msg . done ) msg . session = { ...msg . session , isRetry : false }
155
- setSession ( msg . session )
245
+ if ( useForegroundFetch ) {
246
+ return ( ) => { }
247
+ } else {
248
+ port . onMessage . addListener ( portMessageListener )
249
+ return ( ) => {
250
+ port . onMessage . removeListener ( portMessageListener )
156
251
}
157
- if ( msg . done ) {
158
- updateAnswer ( '' , true , 'answer' , true )
159
- setIsReady ( true )
160
- }
161
- if ( msg . error ) {
162
- switch ( msg . error ) {
163
- case 'UNAUTHORIZED' :
164
- updateAnswer (
165
- `${ t ( 'UNAUTHORIZED' ) } <br>${ t ( 'Please login at https://chat.openai.com first' ) } ${
166
- isSafari ( ) ? `<br>${ t ( 'Then open https://chat.openai.com/api/auth/session' ) } ` : ''
167
- } <br>${ t ( 'And refresh this page or type you question again' ) } ` +
168
- `<br><br>${ t (
169
- 'Consider creating an api key at https://platform.openai.com/account/api-keys' ,
170
- ) } `,
171
- false ,
172
- 'error' ,
173
- )
174
- break
175
- case 'CLOUDFLARE' :
176
- updateAnswer (
177
- `${ t ( 'OpenAI Security Check Required' ) } <br>${
178
- isSafari ( )
179
- ? t ( 'Please open https://chat.openai.com/api/auth/session' )
180
- : t ( 'Please open https://chat.openai.com' )
181
- } <br>${ t ( 'And refresh this page or type you question again' ) } ` +
182
- `<br><br>${ t (
183
- 'Consider creating an api key at https://platform.openai.com/account/api-keys' ,
184
- ) } `,
185
- false ,
186
- 'error' ,
187
- )
188
- break
189
- default :
190
- if (
191
- conversationItemData [ conversationItemData . length - 1 ] . content . includes ( 'gpt-loading' )
192
- )
193
- updateAnswer ( msg . error , false , 'error' )
194
- else
195
- setConversationItemData ( [
196
- ...conversationItemData ,
197
- new ConversationItemData ( 'error' , msg . error ) ,
198
- ] )
199
- break
200
- }
201
- setIsReady ( true )
202
- }
203
- }
204
- port . onMessage . addListener ( listener )
205
- return ( ) => {
206
- port . onMessage . removeListener ( listener )
207
252
}
208
253
} , [ conversationItemData ] )
209
254
210
- const getRetryFn = ( session ) => ( ) => {
255
+ const getRetryFn = ( session ) => async ( ) => {
211
256
updateAnswer ( `<p class="gpt-loading">${ t ( 'Waiting for response...' ) } </p>` , false , 'answer' )
212
257
setIsReady ( false )
213
258
@@ -223,8 +268,8 @@ function ConversationCard(props) {
223
268
const newSession = { ...session , isRetry : true }
224
269
setSession ( newSession )
225
270
try {
226
- port . postMessage ( { stop : true } )
227
- port . postMessage ( { session : newSession } )
271
+ await postMessage ( { stop : true } )
272
+ await postMessage ( { session : newSession } )
228
273
} catch ( e ) {
229
274
updateAnswer ( e , false , 'error' )
230
275
}
@@ -348,8 +393,8 @@ function ConversationCard(props) {
348
393
< DeleteButton
349
394
size = { 16 }
350
395
text = { t ( 'Clear Conversation' ) }
351
- onConfirm = { ( ) => {
352
- port . postMessage ( { stop : true } )
396
+ onConfirm = { async ( ) => {
397
+ await postMessage ( { stop : true } )
353
398
Browser . runtime . sendMessage ( {
354
399
type : 'DELETE_CONVERSATION' ,
355
400
data : {
@@ -449,7 +494,7 @@ function ConversationCard(props) {
449
494
enabled = { isReady }
450
495
port = { port }
451
496
reverseResizeDir = { props . pageMode }
452
- onSubmit = { ( question ) => {
497
+ onSubmit = { async ( question ) => {
453
498
const newQuestion = new ConversationItemData ( 'question' , question )
454
499
const newAnswer = new ConversationItemData (
455
500
'answer' ,
@@ -461,7 +506,7 @@ function ConversationCard(props) {
461
506
const newSession = { ...session , question, isRetry : false }
462
507
setSession ( newSession )
463
508
try {
464
- port . postMessage ( { session : newSession } )
509
+ await postMessage ( { session : newSession } )
465
510
} catch ( e ) {
466
511
updateAnswer ( e , false , 'error' )
467
512
}
0 commit comments