11import type { MouseEventHandler } from 'react' ;
2- import { useRef , useState } from 'react' ;
2+ import { useRef } from 'react' ;
33
44import { useForm } from '../../components/form' ;
55import { useNow } from '../../hooks/use-now' ;
66import { useLocale } from '../../localization' ;
7+ import { setup } from '../../machine/setup' ;
8+ import { useMachine } from '../../machine/use-machine' ;
79import { formatDate } from '../../utils/format-date' ;
810import { getExpirationDate } from './api-keys-table.format' ;
911import type { APIKeysTableMessages } from './api-keys-table.types' ;
@@ -28,6 +30,58 @@ export interface CreateAPIKeyController {
2830 dialog : CreateAPIKeyDialogProps ;
2931}
3032
33+ interface Context {
34+ secret : string | null ;
35+ copyFailed : boolean ;
36+ }
37+
38+ type Event =
39+ | { type : 'OPEN' }
40+ | { type : 'CREATED' ; secret : string }
41+ | { type : 'COPY' }
42+ | { type : 'COPIED' }
43+ | { type : 'COPY_FAILED' }
44+ | { type : 'CLOSE' } ;
45+
46+ const { createMachine, assign } = setup < Context , Event > ( ) ;
47+
48+ const clearCopyError = assign ( ( ) => ( { copyFailed : false } ) ) ;
49+ const setCopyError = assign ( ( ) => ( { copyFailed : true } ) ) ;
50+
51+ const machine = createMachine ( {
52+ id : 'createAPIKey' ,
53+ initial : 'closed' ,
54+ context : { secret : null , copyFailed : false } ,
55+ states : {
56+ closed : {
57+ on : { OPEN : { target : 'form' , actions : assign ( ( ) => ( { secret : null , copyFailed : false } ) ) } } ,
58+ } ,
59+ form : {
60+ on : {
61+ CREATED : { target : 'secret' , actions : assign ( ( _ , event ) => ( { secret : event . secret } ) ) } ,
62+ CLOSE : 'closed' ,
63+ } ,
64+ } ,
65+ secret : {
66+ on : { COPY : 'copying' } ,
67+ } ,
68+ copying : {
69+ on : {
70+ COPIED : { target : 'attempted' , actions : clearCopyError } ,
71+ COPY_FAILED : { target : 'attempted' , actions : setCopyError } ,
72+ } ,
73+ } ,
74+ attempted : {
75+ on : {
76+ COPY : 'copying' ,
77+ COPIED : { target : 'attempted' , actions : clearCopyError } ,
78+ COPY_FAILED : { target : 'attempted' , actions : setCopyError } ,
79+ CLOSE : 'closed' ,
80+ } ,
81+ } ,
82+ } ,
83+ } ) ;
84+
3185export function useCreateAPIKeyController ( {
3286 messages,
3387 showDescription,
@@ -36,10 +90,8 @@ export function useCreateAPIKeyController({
3690 const locale = useLocale ( ) ;
3791 const now = useNow ( { updateInterval : 60_000 } ) ;
3892 const trigger = useRef < HTMLButtonElement | null > ( null ) ;
39- const [ open , setOpen ] = useState ( false ) ;
40- const [ secret , setSecret ] = useState < string | null > ( null ) ;
41- const [ copyError , setCopyError ] = useState < string | null > ( null ) ;
42- const [ hasTriedCopy , setHasTriedCopy ] = useState ( false ) ;
93+ const [ snapshot , send ] = useMachine ( machine ) ;
94+ const { secret, copyFailed } = snapshot . context ;
4395
4496 const form = useForm ( {
4597 initialValues,
@@ -57,7 +109,7 @@ export function useCreateAPIKeyController({
57109 } ,
58110 onSuccess : secret => {
59111 if ( secret !== undefined ) {
60- setSecret ( secret ) ;
112+ send ( { type : 'CREATED' , secret } ) ;
61113 }
62114 } ,
63115 } ) ;
@@ -69,40 +121,36 @@ export function useCreateAPIKeyController({
69121 onOpen : event => {
70122 trigger . current = event . currentTarget ;
71123 form . reset ( ) ;
72- setSecret ( null ) ;
73- setCopyError ( null ) ;
74- setHasTriedCopy ( false ) ;
75- setOpen ( true ) ;
124+ send ( { type : 'OPEN' } ) ;
76125 } ,
77126 dialog : {
78127 messages,
79- open,
128+ open : snapshot . value !== 'closed' ,
80129 onOpenChange : next => {
81- if ( ! form . isSubmitting && ( secret === null || hasTriedCopy ) ) {
82- setOpen ( next ) ;
130+ if ( ! next && ! form . isSubmitting ) {
131+ send ( { type : 'CLOSE' } ) ;
83132 }
84133 } ,
85134 finalFocus : trigger ,
86135 form,
87136 showDescription,
88137 expirationDateLabel : expirationDate ? formatDate ( expirationDate , locale ) : null ,
89138 secret,
90- copyError,
139+ copyError : copyFailed ? messages . copyError : null ,
91140 onCopy : async close => {
141+ send ( { type : 'COPY' } ) ;
92142 try {
93143 await navigator . clipboard . writeText ( secret ?? '' ) ;
94144 } catch ( error ) {
95- setHasTriedCopy ( true ) ;
96- setCopyError ( messages . copyError ) ;
145+ send ( { type : 'COPY_FAILED' } ) ;
97146 if ( ! close ) {
98147 throw error ;
99148 }
100149 return ;
101150 }
102- setHasTriedCopy ( true ) ;
103- setCopyError ( null ) ;
151+ send ( { type : 'COPIED' } ) ;
104152 if ( close ) {
105- setOpen ( false ) ;
153+ send ( { type : 'CLOSE' } ) ;
106154 }
107155 } ,
108156 } ,
0 commit comments