@@ -21,6 +21,7 @@ import {
21
21
} from "@webstudio-is/icons" ;
22
22
import { CollapsibleDomainSection } from "./collapsible-domain-section" ;
23
23
import {
24
+ Fragment ,
24
25
startTransition ,
25
26
useEffect ,
26
27
useOptimistic ,
@@ -29,17 +30,18 @@ import {
29
30
type ReactNode ,
30
31
} from "react" ;
31
32
import { Entri } from "./entri" ;
32
- import { nativeClient } from "~/shared/trpc/trpc-client" ;
33
+ import { nativeClient , trpcClient } from "~/shared/trpc/trpc-client" ;
33
34
import { useStore } from "@nanostores/react" ;
34
35
import { $publisherHost } from "~/shared/nano-states" ;
35
36
import { extractCname } from "./cname" ;
36
37
import { useEffectEvent } from "~/shared/hook-utils/effect-event" ;
37
- import DomainCheckbox from "./domain-checkbox" ;
38
+ import { DomainCheckbox } from "./domain-checkbox" ;
38
39
import { CopyToClipboard } from "~/builder/shared/copy-to-clipboard" ;
39
40
import { RelativeTime } from "~/builder/shared/relative-time" ;
40
41
41
42
export type Domain = Project [ "domainsVirtual" ] [ number ] ;
42
- type DomainStatus = Project [ "domainsVirtual" ] [ number ] [ "status" ] ;
43
+
44
+ type DomainStatus = Domain [ "status" ] ;
43
45
44
46
const InputEllipsis = styled ( InputField , {
45
47
"&>input" : {
@@ -164,6 +166,119 @@ const StatusIcon = (props: { projectDomain: Domain; isLoading: boolean }) => {
164
166
) ;
165
167
} ;
166
168
169
+ const DomainConfig = ( {
170
+ projectDomain,
171
+ onUpdateStatus,
172
+ } : {
173
+ projectDomain : Domain ;
174
+ onUpdateStatus : ( ) => void ;
175
+ } ) => {
176
+ const { load : findDomainRegistrar , data : registrar } =
177
+ trpcClient . domain . findDomainRegistrar . useQuery ( ) ;
178
+ const cname = extractCname ( projectDomain . domain ) ;
179
+ useEffect ( ( ) => {
180
+ if ( cname === "@" ) {
181
+ findDomainRegistrar ( { domain : projectDomain . domain } ) ;
182
+ }
183
+ } , [ projectDomain . domain , cname ] ) ;
184
+ const publisherHost = useStore ( $publisherHost ) ;
185
+
186
+ const cnameRecord = {
187
+ // use alias for domain root when supported to avoid conflicting
188
+ // with MX, NS etc records
189
+ type : cname === "@" && registrar ?. alias ? "ALIAS" : "CNAME" ,
190
+ host : cname ,
191
+ value : `${ projectDomain . cname } .customers.${ publisherHost } ` ,
192
+ ttl : 300 ,
193
+ } as const ;
194
+
195
+ const txtRecord = {
196
+ type : "TXT" ,
197
+ host : cname === "@" ? "_webstudio_is" : `_webstudio_is.${ cname } ` ,
198
+ value : projectDomain . expectedTxtRecord ,
199
+ ttl : 300 ,
200
+ } as const ;
201
+
202
+ const dnsRecords = [ cnameRecord , txtRecord ] ;
203
+
204
+ return (
205
+ < >
206
+ < Text color = "subtle" >
207
+ < strong > To verify your domain:</ strong >
208
+ < br />
209
+ Visit the admin console of your domain registrar (the website you
210
+ purchased your domain from) and create one < strong > CNAME</ strong > record
211
+ and one < strong > TXT</ strong > record with the values shown below:
212
+ </ Text >
213
+
214
+ < Grid
215
+ gap = { 2 }
216
+ css = { { gridTemplateColumns : `${ theme . spacing [ 18 ] } 1fr 1fr` } }
217
+ >
218
+ < Text color = "subtle" variant = "titles" >
219
+ TYPE
220
+ </ Text >
221
+ < Text color = "subtle" variant = "titles" >
222
+ NAME
223
+ </ Text >
224
+ < Text color = "subtle" variant = "titles" >
225
+ VALUE
226
+ </ Text >
227
+
228
+ { dnsRecords . map ( ( record , index ) => (
229
+ < Fragment key = { index } >
230
+ < InputEllipsis readOnly value = { record . type } />
231
+ < InputEllipsis
232
+ readOnly
233
+ value = { record . host }
234
+ suffix = {
235
+ < CopyToClipboard text = { record . host } >
236
+ < NestedInputButton type = "button" >
237
+ < CopyIcon />
238
+ </ NestedInputButton >
239
+ </ CopyToClipboard >
240
+ }
241
+ />
242
+ < InputEllipsis
243
+ readOnly
244
+ value = { record . value }
245
+ suffix = {
246
+ < CopyToClipboard text = { record . value } >
247
+ < NestedInputButton type = "button" >
248
+ < CopyIcon />
249
+ </ NestedInputButton >
250
+ </ CopyToClipboard >
251
+ }
252
+ />
253
+ </ Fragment >
254
+ ) ) }
255
+ </ Grid >
256
+
257
+ < Grid
258
+ gap = { 2 }
259
+ align = { "center" }
260
+ css = { {
261
+ gridTemplateColumns : `1fr auto 1fr` ,
262
+ } }
263
+ >
264
+ < Separator css = { { alignSelf : "unset" } } />
265
+ < Text color = "main" > OR</ Text >
266
+ < Separator css = { { alignSelf : "unset" } } />
267
+ </ Grid >
268
+
269
+ < Entri
270
+ dnsRecords = { dnsRecords }
271
+ domain = { projectDomain . domain }
272
+ onClose = { ( ) => {
273
+ // Sometimes Entri modal dialog hangs even if it's successful,
274
+ // until they fix that, we'll just refresh the status here on every onClose event
275
+ onUpdateStatus ( ) ;
276
+ } }
277
+ />
278
+ </ >
279
+ ) ;
280
+ } ;
281
+
167
282
const DomainItem = ( props : {
168
283
initiallyOpen : boolean ;
169
284
projectDomain : Domain ;
@@ -272,33 +387,8 @@ const DomainItem = (props: {
272
387
} ) ;
273
388
} , [ status , handleVerify , handleUpdateStatus , isStatusLoading ] ) ;
274
389
275
- const publisherHost = useStore ( $publisherHost ) ;
276
- const cnameEntryName = extractCname ( props . projectDomain . domain ) ;
277
- const cnameEntryValue = `${ props . projectDomain . cname } .customers.${ publisherHost } ` ;
278
-
279
- const txtEntryName =
280
- cnameEntryName === "@"
281
- ? "_webstudio_is"
282
- : `_webstudio_is.${ cnameEntryName } ` ;
283
-
284
390
const domainStatus = getStatus ( props . projectDomain ) ;
285
391
286
- const cnameRecord = {
287
- type : "CNAME" ,
288
- host : cnameEntryName ,
289
- value : cnameEntryValue ,
290
- ttl : 300 ,
291
- } as const ;
292
-
293
- const txtRecord = {
294
- type : "TXT" ,
295
- host : txtEntryName ,
296
- value : props . projectDomain . expectedTxtRecord ,
297
- ttl : 300 ,
298
- } as const ;
299
-
300
- const dnsRecords = [ cnameRecord , txtRecord ] ;
301
-
302
392
const { isVerifiedActive, text } = getStatusText ( {
303
393
projectDomain : props . projectDomain ,
304
394
isLoading : false ,
@@ -413,98 +503,9 @@ const DomainItem = (props: {
413
503
) }
414
504
</ Grid >
415
505
416
- < Text color = "subtle" >
417
- < strong > To verify your domain:</ strong >
418
- < br />
419
- Visit the admin console of your domain registrar (the website you
420
- purchased your domain from) and create one < strong > CNAME</ strong > { " " }
421
- record and one < strong > TXT</ strong > record with the values shown
422
- below:
423
- </ Text >
424
-
425
- < Grid
426
- gap = { 2 }
427
- css = { {
428
- gridTemplateColumns : `${ theme . spacing [ 18 ] } 1fr 1fr` ,
429
- } }
430
- >
431
- < Text color = "subtle" variant = { "titles" } >
432
- TYPE
433
- </ Text >
434
- < Text color = "subtle" variant = { "titles" } >
435
- NAME
436
- </ Text >
437
- < Text color = "subtle" variant = { "titles" } >
438
- VALUE
439
- </ Text >
440
-
441
- < InputEllipsis readOnly value = "CNAME" />
442
- < InputEllipsis
443
- readOnly
444
- value = { cnameRecord . host }
445
- suffix = {
446
- < CopyToClipboard text = { cnameRecord . host } >
447
- < NestedInputButton type = "button" >
448
- < CopyIcon />
449
- </ NestedInputButton >
450
- </ CopyToClipboard >
451
- }
452
- />
453
- < InputEllipsis
454
- readOnly
455
- value = { cnameRecord . value }
456
- suffix = {
457
- < CopyToClipboard text = { cnameRecord . value } >
458
- < NestedInputButton type = "button" >
459
- < CopyIcon />
460
- </ NestedInputButton >
461
- </ CopyToClipboard >
462
- }
463
- />
464
-
465
- < InputEllipsis readOnly value = "TXT" />
466
- < InputEllipsis
467
- readOnly
468
- value = { txtRecord . host }
469
- suffix = {
470
- < CopyToClipboard text = { txtRecord . host } >
471
- < NestedInputButton type = "button" >
472
- < CopyIcon />
473
- </ NestedInputButton >
474
- </ CopyToClipboard >
475
- }
476
- />
477
- < InputEllipsis
478
- readOnly
479
- value = { txtRecord . value }
480
- suffix = {
481
- < CopyToClipboard text = { txtRecord . value } >
482
- < NestedInputButton type = "button" >
483
- < CopyIcon />
484
- </ NestedInputButton >
485
- </ CopyToClipboard >
486
- }
487
- />
488
- </ Grid >
489
-
490
- < Grid
491
- gap = { 2 }
492
- align = { "center" }
493
- css = { {
494
- gridTemplateColumns : `1fr auto 1fr` ,
495
- } }
496
- >
497
- < Separator css = { { alignSelf : "unset" } } />
498
- < Text color = "main" > OR</ Text >
499
- < Separator css = { { alignSelf : "unset" } } />
500
- </ Grid >
501
-
502
- < Entri
503
- dnsRecords = { dnsRecords }
504
- domain = { props . projectDomain . domain }
505
- onClose = { ( ) => {
506
- // Sometimes Entri modal dialog hangs even if it's successful,
507
- // until they fix that, we'll just refresh the status here on every onClose event
506
+ < DomainConfig
507
+ projectDomain = { props . projectDomain }
508
+ onUpdateStatus = { ( ) => {
508
509
if ( status === "UNVERIFIED" ) {
509
510
startTransition ( async ( ) => {
510
511
await handleVerify ( ) ;
0 commit comments