@@ -15,15 +15,13 @@ import { Link } from "@/components/link"
1515import { useLanguage } from "@/context/language"
1616import { useGlobalSDK } from "@/context/global-sdk"
1717import { useGlobalSync } from "@/context/global-sync"
18- import { usePlatform } from "@/context/platform"
1918import { DialogSelectModel } from "./dialog-select-model"
2019import { DialogSelectProvider } from "./dialog-select-provider"
2120
2221export function DialogConnectProvider ( props : { provider : string } ) {
2322 const dialog = useDialog ( )
2423 const globalSync = useGlobalSync ( )
2524 const globalSDK = useGlobalSDK ( )
26- const platform = usePlatform ( )
2725 const language = useLanguage ( )
2826
2927 const alive = { value : true }
@@ -49,13 +47,14 @@ export function DialogConnectProvider(props: { provider: string }) {
4947 const [ store , setStore ] = createStore ( {
5048 methodIndex : undefined as undefined | number ,
5149 authorization : undefined as undefined | ProviderAuthAuthorization ,
52- state : "pending" as undefined | "pending" | "complete" | "error" ,
50+ state : "pending" as undefined | "pending" | "complete" | "error" | "prompt" ,
5351 error : undefined as string | undefined ,
5452 } )
5553
5654 type Action =
5755 | { type : "method.select" ; index : number }
5856 | { type : "method.reset" }
57+ | { type : "auth.prompt" }
5958 | { type : "auth.pending" }
6059 | { type : "auth.complete" ; authorization : ProviderAuthAuthorization }
6160 | { type : "auth.error" ; error : string }
@@ -77,6 +76,11 @@ export function DialogConnectProvider(props: { provider: string }) {
7776 draft . error = undefined
7877 return
7978 }
79+ if ( action . type === "auth.prompt" ) {
80+ draft . state = "prompt"
81+ draft . error = undefined
82+ return
83+ }
8084 if ( action . type === "auth.pending" ) {
8185 draft . state = "pending"
8286 draft . error = undefined
@@ -120,7 +124,7 @@ export function DialogConnectProvider(props: { provider: string }) {
120124 return fallback
121125 }
122126
123- async function selectMethod ( index : number ) {
127+ async function selectMethod ( index : number , inputs ?: Record < string , string > ) {
124128 if ( timer . current !== undefined ) {
125129 clearTimeout ( timer . current )
126130 timer . current = undefined
@@ -130,13 +134,18 @@ export function DialogConnectProvider(props: { provider: string }) {
130134 dispatch ( { type : "method.select" , index } )
131135
132136 if ( method . type === "oauth" ) {
137+ if ( method . prompts ?. length && ! inputs ) {
138+ dispatch ( { type : "auth.prompt" } )
139+ return
140+ }
133141 dispatch ( { type : "auth.pending" } )
134142 const start = Date . now ( )
135143 await globalSDK . client . provider . oauth
136144 . authorize (
137145 {
138146 providerID : props . provider ,
139147 method : index ,
148+ inputs,
140149 } ,
141150 { throwOnError : true } ,
142151 )
@@ -163,6 +172,122 @@ export function DialogConnectProvider(props: { provider: string }) {
163172 }
164173 }
165174
175+ function OAuthPromptsView ( ) {
176+ const [ formStore , setFormStore ] = createStore ( {
177+ value : { } as Record < string , string > ,
178+ index : 0 ,
179+ } )
180+
181+ const prompts = createMemo ( ( ) => method ( ) ?. prompts ?? [ ] )
182+ const matches = ( prompt : NonNullable < ReturnType < typeof prompts > [ number ] > , value : Record < string , string > ) => {
183+ if ( ! prompt . when ) return true
184+ const actual = value [ prompt . when . key ]
185+ if ( actual === undefined ) return false
186+ return prompt . when . op === "eq" ? actual === prompt . when . value : actual !== prompt . when . value
187+ }
188+ const current = createMemo ( ( ) => {
189+ const all = prompts ( )
190+ const index = all . findIndex ( ( prompt , index ) => index >= formStore . index && matches ( prompt , formStore . value ) )
191+ if ( index === - 1 ) return
192+ return {
193+ index,
194+ prompt : all [ index ] ,
195+ }
196+ } )
197+ const valid = createMemo ( ( ) => {
198+ const item = current ( )
199+ if ( ! item || item . prompt . type !== "text" ) return false
200+ const value = formStore . value [ item . prompt . key ] ?? ""
201+ return value . trim ( ) . length > 0
202+ } )
203+
204+ async function next ( index : number , value : Record < string , string > ) {
205+ if ( store . methodIndex === undefined ) return
206+ const next = prompts ( ) . findIndex ( ( prompt , i ) => i > index && matches ( prompt , value ) )
207+ if ( next !== - 1 ) {
208+ setFormStore ( "index" , next )
209+ return
210+ }
211+ await selectMethod ( store . methodIndex , value )
212+ }
213+
214+ async function handleSubmit ( e : SubmitEvent ) {
215+ e . preventDefault ( )
216+ const item = current ( )
217+ if ( ! item || item . prompt . type !== "text" ) return
218+ if ( ! valid ( ) ) return
219+ await next ( item . index , formStore . value )
220+ }
221+
222+ const item = ( ) => current ( )
223+ const text = createMemo ( ( ) => {
224+ const prompt = item ( ) ?. prompt
225+ if ( ! prompt || prompt . type !== "text" ) return
226+ return prompt
227+ } )
228+ const select = createMemo ( ( ) => {
229+ const prompt = item ( ) ?. prompt
230+ if ( ! prompt || prompt . type !== "select" ) return
231+ return prompt
232+ } )
233+
234+ return (
235+ < form onSubmit = { handleSubmit } class = "flex flex-col items-start gap-4" >
236+ < Switch >
237+ < Match when = { item ( ) ?. prompt . type === "text" } >
238+ < TextField
239+ type = "text"
240+ label = { text ( ) ?. message ?? "" }
241+ placeholder = { text ( ) ?. placeholder }
242+ value = { text ( ) ? ( formStore . value [ text ( ) ! . key ] ?? "" ) : "" }
243+ onChange = { ( value ) => {
244+ const prompt = text ( )
245+ if ( ! prompt ) return
246+ setFormStore ( "value" , prompt . key , value )
247+ } }
248+ />
249+ < Button class = "w-auto" type = "submit" size = "large" variant = "primary" disabled = { ! valid ( ) } >
250+ { language . t ( "common.continue" ) }
251+ </ Button >
252+ </ Match >
253+ < Match when = { item ( ) ?. prompt . type === "select" } >
254+ < div class = "w-full flex flex-col gap-1.5" >
255+ < div class = "text-14-regular text-text-base" > { select ( ) ?. message } </ div >
256+ < div >
257+ < List
258+ items = { select ( ) ?. options ?? [ ] }
259+ key = { ( x ) => x . value }
260+ current = { select ( ) ?. options . find ( ( x ) => x . value === formStore . value [ select ( ) ! . key ] ) }
261+ onSelect = { ( value ) => {
262+ if ( ! value ) return
263+ const prompt = select ( )
264+ if ( ! prompt ) return
265+ const nextValue = {
266+ ...formStore . value ,
267+ [ prompt . key ] : value . value ,
268+ }
269+ setFormStore ( "value" , prompt . key , value . value )
270+ void next ( item ( ) ! . index , nextValue )
271+ } }
272+ >
273+ { ( option ) => (
274+ < div class = "w-full flex items-center gap-x-2" >
275+ < div class = "w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center" >
276+ < div class = "w-2.5 h-0.5 ml-0 bg-icon-strong-base hidden" data-slot = "list-item-extra-icon" />
277+ </ div >
278+ < span > { option . label } </ span >
279+ < span class = "text-14-regular text-text-weak" > { option . hint } </ span >
280+ </ div >
281+ ) }
282+ </ List >
283+ </ div >
284+ </ div >
285+ </ Match >
286+ </ Switch >
287+ </ form >
288+ )
289+ }
290+
166291 let listRef : ListRef | undefined
167292 function handleKey ( e : KeyboardEvent ) {
168293 if ( e . key === "Enter" && e . target instanceof HTMLInputElement ) {
@@ -301,7 +426,7 @@ export function DialogConnectProvider(props: { provider: string }) {
301426 error = { formStore . error }
302427 />
303428 < Button class = "w-auto" type = "submit" size = "large" variant = "primary" >
304- { language . t ( "common.submit " ) }
429+ { language . t ( "common.continue " ) }
305430 </ Button >
306431 </ form >
307432 </ div >
@@ -314,12 +439,6 @@ export function DialogConnectProvider(props: { provider: string }) {
314439 error : undefined as string | undefined ,
315440 } )
316441
317- onMount ( ( ) => {
318- if ( store . authorization ?. method === "code" && store . authorization ?. url ) {
319- platform . openLink ( store . authorization . url )
320- }
321- } )
322-
323442 async function handleSubmit ( e : SubmitEvent ) {
324443 e . preventDefault ( )
325444
@@ -368,7 +487,7 @@ export function DialogConnectProvider(props: { provider: string }) {
368487 error = { formStore . error }
369488 />
370489 < Button class = "w-auto" type = "submit" size = "large" variant = "primary" >
371- { language . t ( "common.submit " ) }
490+ { language . t ( "common.continue " ) }
372491 </ Button >
373492 </ form >
374493 </ div >
@@ -386,10 +505,6 @@ export function DialogConnectProvider(props: { provider: string }) {
386505
387506 onMount ( ( ) => {
388507 void ( async ( ) => {
389- if ( store . authorization ?. url ) {
390- platform . openLink ( store . authorization . url )
391- }
392-
393508 const result = await globalSDK . client . provider . oauth
394509 . callback ( {
395510 providerID : props . provider ,
@@ -470,6 +585,9 @@ export function DialogConnectProvider(props: { provider: string }) {
470585 </ div >
471586 </ div >
472587 </ Match >
588+ < Match when = { store . state === "prompt" } >
589+ < OAuthPromptsView />
590+ </ Match >
473591 < Match when = { store . state === "error" } >
474592 < div class = "text-14-regular text-text-base" >
475593 < div class = "flex items-center gap-x-2" >
0 commit comments