@@ -12,63 +12,45 @@ import { skipToken } from '@reduxjs/toolkit/query';
12
12
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus' ;
13
13
import { toast } from 'features/toast/toast' ;
14
14
import type { ChangeEvent } from 'react' ;
15
- import { useCallback , useMemo , useState } from 'react' ;
15
+ import { memo , useCallback , useMemo , useState } from 'react' ;
16
16
import { useTranslation } from 'react-i18next' ;
17
- import { useGetHFTokenStatusQuery , useSetHFTokenMutation } from 'services/api/endpoints/models' ;
18
- import { UNAUTHORIZED_TOAST_ID } from 'services/events/onModelInstallError' ;
17
+ import {
18
+ useGetHFTokenStatusQuery ,
19
+ useResetHFTokenMutation ,
20
+ useSetHFTokenMutation ,
21
+ } from 'services/api/endpoints/models' ;
22
+ import type { Equals } from 'tsafe' ;
23
+ import { assert } from 'tsafe' ;
19
24
20
25
export const HFToken = ( ) => {
21
26
const { t } = useTranslation ( ) ;
22
27
const isHFTokenEnabled = useFeatureStatus ( 'hfToken' ) ;
23
- const [ token , setToken ] = useState ( '' ) ;
24
28
const { currentData } = useGetHFTokenStatusQuery ( isHFTokenEnabled ? undefined : skipToken ) ;
25
- const [ trigger , { isLoading, isUninitialized } ] = useSetHFTokenMutation ( ) ;
26
- const onChange = useCallback ( ( e : ChangeEvent < HTMLInputElement > ) => {
27
- setToken ( e . target . value ) ;
28
- } , [ ] ) ;
29
- const onClick = useCallback ( ( ) => {
30
- trigger ( { token } )
31
- . unwrap ( )
32
- . then ( ( res ) => {
33
- if ( res === 'valid' ) {
34
- setToken ( '' ) ;
35
- toast ( {
36
- id : UNAUTHORIZED_TOAST_ID ,
37
- title : t ( 'modelManager.hfTokenSaved' ) ,
38
- status : 'success' ,
39
- duration : 3000 ,
40
- } ) ;
41
- }
42
- } ) ;
43
- } , [ t , token , trigger ] ) ;
44
29
45
30
const error = useMemo ( ( ) => {
46
- if ( ! currentData || isUninitialized || isLoading ) {
47
- return null ;
48
- }
49
- if ( currentData === 'invalid' ) {
50
- return t ( 'modelManager.hfTokenInvalidErrorMessage' ) ;
51
- }
52
- if ( currentData === 'unknown' ) {
53
- return t ( 'modelManager.hfTokenUnableToVerifyErrorMessage' ) ;
31
+ switch ( currentData ) {
32
+ case 'invalid' :
33
+ return t ( 'modelManager.hfTokenInvalidErrorMessage' ) ;
34
+ case 'unknown' :
35
+ return t ( 'modelManager.hfTokenUnableToVerifyErrorMessage' ) ;
36
+ case 'valid' :
37
+ case undefined :
38
+ return null ;
39
+ default :
40
+ assert < Equals < never , typeof currentData > > ( false , 'Unexpected HF token status' ) ;
54
41
}
55
- return null ;
56
- } , [ currentData , isLoading , isUninitialized , t ] ) ;
42
+ } , [ currentData , t ] ) ;
57
43
58
- if ( ! currentData || currentData === 'valid' ) {
44
+ if ( ! currentData ) {
59
45
return null ;
60
46
}
61
47
62
48
return (
63
49
< Flex borderRadius = "base" w = "full" >
64
- < FormControl isInvalid = { ! isUninitialized && Boolean ( error ) } orientation = "vertical" >
50
+ < FormControl isInvalid = { Boolean ( error ) } orientation = "vertical" >
65
51
< FormLabel > { t ( 'modelManager.hfTokenLabel' ) } </ FormLabel >
66
- < Flex gap = { 3 } alignItems = "center" w = "full" >
67
- < Input type = "password" value = { token } onChange = { onChange } />
68
- < Button onClick = { onClick } size = "sm" isDisabled = { token . trim ( ) . length === 0 } isLoading = { isLoading } >
69
- { t ( 'common.save' ) }
70
- </ Button >
71
- </ Flex >
52
+ { error && < SetHFTokenInput /> }
53
+ { ! error && < ResetHFTokenButton /> }
72
54
< FormHelperText >
73
55
< ExternalLink label = { t ( 'modelManager.hfTokenHelperText' ) } href = "https://huggingface.co/settings/tokens" />
74
56
</ FormHelperText >
@@ -77,3 +59,73 @@ export const HFToken = () => {
77
59
</ Flex >
78
60
) ;
79
61
} ;
62
+
63
+ const PLACEHOLDER_TOKEN = Array . from ( { length : 37 } , ( ) => 'a' ) . join ( '' ) ;
64
+
65
+ const ResetHFTokenButton = memo ( ( ) => {
66
+ const { t } = useTranslation ( ) ;
67
+ const [ resetHFToken , { isLoading } ] = useResetHFTokenMutation ( ) ;
68
+
69
+ const onClick = useCallback ( ( ) => {
70
+ resetHFToken ( )
71
+ . unwrap ( )
72
+ . then ( ( ) => {
73
+ toast ( {
74
+ title : t ( 'modelManager.hfTokenReset' ) ,
75
+ status : 'info' ,
76
+ } ) ;
77
+ } ) ;
78
+ } , [ resetHFToken , t ] ) ;
79
+
80
+ return (
81
+ < Flex gap = { 3 } alignItems = "center" w = "full" >
82
+ < Input type = "password" value = { PLACEHOLDER_TOKEN } isDisabled />
83
+ < Button onClick = { onClick } size = "sm" isLoading = { isLoading } >
84
+ { t ( 'common.reset' ) }
85
+ </ Button >
86
+ </ Flex >
87
+ ) ;
88
+ } ) ;
89
+ ResetHFTokenButton . displayName = 'ResetHFTokenButton' ;
90
+
91
+ const SetHFTokenInput = memo ( ( ) => {
92
+ const { t } = useTranslation ( ) ;
93
+ const [ token , setToken ] = useState ( '' ) ;
94
+ const [ trigger , { isLoading } ] = useSetHFTokenMutation ( ) ;
95
+ const onChange = useCallback ( ( e : ChangeEvent < HTMLInputElement > ) => {
96
+ setToken ( e . target . value ) ;
97
+ } , [ ] ) ;
98
+ const onClick = useCallback ( ( ) => {
99
+ trigger ( { token } )
100
+ . unwrap ( )
101
+ . then ( ( res ) => {
102
+ switch ( res ) {
103
+ case 'valid' :
104
+ setToken ( '' ) ;
105
+ toast ( {
106
+ title : t ( 'modelManager.hfTokenSaved' ) ,
107
+ status : 'success' ,
108
+ } ) ;
109
+ break ;
110
+ case 'invalid' :
111
+ case 'unknown' :
112
+ default :
113
+ toast ( {
114
+ title : t ( 'modelManager.hfTokenUnableToVerify' ) ,
115
+ status : 'error' ,
116
+ } ) ;
117
+ break ;
118
+ }
119
+ } ) ;
120
+ } , [ t , token , trigger ] ) ;
121
+
122
+ return (
123
+ < Flex gap = { 3 } alignItems = "center" w = "full" >
124
+ < Input type = "password" value = { token } onChange = { onChange } />
125
+ < Button onClick = { onClick } size = "sm" isDisabled = { token . trim ( ) . length === 0 } isLoading = { isLoading } >
126
+ { t ( 'common.save' ) }
127
+ </ Button >
128
+ </ Flex >
129
+ ) ;
130
+ } ) ;
131
+ SetHFTokenInput . displayName = 'SetHFTokenInput' ;
0 commit comments