@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
16
16
limitations under the License.
17
17
*/
18
18
19
- import React from "react" ;
19
+ import React , { useCallback , useContext , useEffect } from "react" ;
20
20
import { HTTPError } from "matrix-js-sdk/src/matrix" ;
21
21
import { logger } from "matrix-js-sdk/src/logger" ;
22
22
@@ -34,70 +34,106 @@ import { SettingsSection } from "../../shared/SettingsSection";
34
34
import SettingsSubsection , { SettingsSubsectionText } from "../../shared/SettingsSubsection" ;
35
35
import { SDKContext } from "../../../../../contexts/SDKContext" ;
36
36
import UserPersonalInfoSettings from "../../UserPersonalInfoSettings" ;
37
+ import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext" ;
37
38
38
39
interface IProps {
39
40
closeSettingsFn : ( ) => void ;
40
41
}
41
42
42
- interface IState {
43
+ interface AccountSectionProps {
43
44
canChangePassword : boolean ;
44
- idServerName ?: string ;
45
- externalAccountManagementUrl ?: string ;
46
- canMake3pidChanges : boolean ;
47
- canSetDisplayName : boolean ;
48
- canSetAvatar : boolean ;
45
+ onPasswordChangeError : ( e : Error ) => void ;
46
+ onPasswordChanged : ( ) => void ;
49
47
}
50
48
51
- export default class GeneralUserSettingsTab extends React . Component < IProps , IState > {
52
- public static contextType = SDKContext ;
53
- public context ! : React . ContextType < typeof SDKContext > ;
54
-
55
- public constructor ( props : IProps , context : React . ContextType < typeof SDKContext > ) {
56
- super ( props , context ) ;
57
- this . context = context ;
58
-
59
- this . state = {
60
- canChangePassword : false ,
61
- canMake3pidChanges : false ,
62
- canSetDisplayName : false ,
63
- canSetAvatar : false ,
64
- } ;
65
-
66
- this . getCapabilities ( ) ;
67
- }
68
-
69
- private async getCapabilities ( ) : Promise < void > {
70
- const cli = this . context . client ! ;
71
-
72
- const capabilities = ( await cli . getCapabilities ( ) ) ?? { } ;
73
- const changePasswordCap = capabilities [ "m.change_password" ] ;
74
-
75
- // You can change your password so long as the capability isn't explicitly disabled. The implicit
76
- // behaviour is you can change your password when the capability is missing or has not-false as
77
- // the enabled flag value.
78
- const canChangePassword = ! changePasswordCap || changePasswordCap [ "enabled" ] !== false ;
79
-
80
- await this . context . oidcClientStore . readyPromise ; // wait for the store to be ready
81
- const externalAccountManagementUrl = this . context . oidcClientStore . accountManagementEndpoint ;
82
- // https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability
83
- // We support as far back as v1.1 which doesn't have m.3pid_changes
84
- // so the behaviour for when it is missing has to be assume true
85
- const canMake3pidChanges = ! capabilities [ "m.3pid_changes" ] || capabilities [ "m.3pid_changes" ] . enabled === true ;
86
-
87
- const canSetDisplayName =
88
- ! capabilities [ "m.set_displayname" ] || capabilities [ "m.set_displayname" ] . enabled === true ;
89
- const canSetAvatar = ! capabilities [ "m.set_avatar_url" ] || capabilities [ "m.set_avatar_url" ] . enabled === true ;
90
-
91
- this . setState ( {
92
- canChangePassword,
93
- externalAccountManagementUrl,
94
- canMake3pidChanges,
95
- canSetDisplayName,
96
- canSetAvatar,
97
- } ) ;
98
- }
49
+ const AccountSection : React . FC < AccountSectionProps > = ( {
50
+ canChangePassword,
51
+ onPasswordChangeError,
52
+ onPasswordChanged,
53
+ } ) => {
54
+ if ( ! canChangePassword ) return < > </ > ;
55
+
56
+ return (
57
+ < >
58
+ < SettingsSubsection
59
+ heading = { _t ( "settings|general|account_section" ) }
60
+ stretchContent
61
+ data-testid = "accountSection"
62
+ >
63
+ < SettingsSubsectionText > { _t ( "settings|general|password_change_section" ) } </ SettingsSubsectionText >
64
+ < ChangePassword
65
+ className = "mx_GeneralUserSettingsTab_section--account_changePassword"
66
+ rowClassName = ""
67
+ buttonKind = "primary"
68
+ onError = { onPasswordChangeError }
69
+ onFinished = { onPasswordChanged }
70
+ />
71
+ </ SettingsSubsection >
72
+ </ >
73
+ ) ;
74
+ } ;
75
+
76
+ interface ManagementSectionProps {
77
+ onDeactivateClicked : ( ) => void ;
78
+ }
99
79
100
- private onPasswordChangeError = ( err : Error ) : void => {
80
+ const ManagementSection : React . FC < ManagementSectionProps > = ( { onDeactivateClicked } ) => {
81
+ return (
82
+ < SettingsSection heading = { _t ( "settings|general|deactivate_section" ) } >
83
+ < SettingsSubsection
84
+ heading = { _t ( "settings|general|account_management_section" ) }
85
+ data-testid = "account-management-section"
86
+ description = { _t ( "settings|general|deactivate_warning" ) }
87
+ >
88
+ < AccessibleButton onClick = { onDeactivateClicked } kind = "danger" >
89
+ { _t ( "settings|general|deactivate_section" ) }
90
+ </ AccessibleButton >
91
+ </ SettingsSubsection >
92
+ </ SettingsSection >
93
+ ) ;
94
+ } ;
95
+
96
+ const GeneralUserSettingsTab : React . FC < IProps > = ( { closeSettingsFn } ) => {
97
+ const [ externalAccountManagementUrl , setExternalAccountManagementUrl ] = React . useState < string | undefined > ( ) ;
98
+ const [ canMake3pidChanges , setCanMake3pidChanges ] = React . useState < boolean > ( false ) ;
99
+ const [ canSetDisplayName , setCanSetDisplayName ] = React . useState < boolean > ( false ) ;
100
+ const [ canSetAvatar , setCanSetAvatar ] = React . useState < boolean > ( false ) ;
101
+ const [ canChangePassword , setCanChangePassword ] = React . useState < boolean > ( false ) ;
102
+
103
+ const cli = useMatrixClientContext ( ) ;
104
+ const sdkContext = useContext ( SDKContext ) ;
105
+
106
+ useEffect ( ( ) => {
107
+ ( async ( ) => {
108
+ const capabilities = ( await cli . getCapabilities ( ) ) ?? { } ;
109
+ const changePasswordCap = capabilities [ "m.change_password" ] ;
110
+
111
+ // You can change your password so long as the capability isn't explicitly disabled. The implicit
112
+ // behaviour is you can change your password when the capability is missing or has not-false as
113
+ // the enabled flag value.
114
+ const canChangePassword = ! changePasswordCap || changePasswordCap [ "enabled" ] !== false ;
115
+
116
+ await sdkContext . oidcClientStore . readyPromise ; // wait for the store to be ready
117
+ const externalAccountManagementUrl = sdkContext . oidcClientStore . accountManagementEndpoint ;
118
+ // https://spec.matrix.org/v1.7/client-server-api/#m3pid_changes-capability
119
+ // We support as far back as v1.1 which doesn't have m.3pid_changes
120
+ // so the behaviour for when it is missing has to be assume true
121
+ const canMake3pidChanges =
122
+ ! capabilities [ "m.3pid_changes" ] || capabilities [ "m.3pid_changes" ] . enabled === true ;
123
+
124
+ const canSetDisplayName =
125
+ ! capabilities [ "m.set_displayname" ] || capabilities [ "m.set_displayname" ] . enabled === true ;
126
+ const canSetAvatar = ! capabilities [ "m.set_avatar_url" ] || capabilities [ "m.set_avatar_url" ] . enabled === true ;
127
+
128
+ setCanMake3pidChanges ( canMake3pidChanges ) ;
129
+ setCanSetDisplayName ( canSetDisplayName ) ;
130
+ setCanSetAvatar ( canSetAvatar ) ;
131
+ setExternalAccountManagementUrl ( externalAccountManagementUrl ) ;
132
+ setCanChangePassword ( canChangePassword ) ;
133
+ } ) ( ) ;
134
+ } , [ cli , sdkContext . oidcClientStore ] ) ;
135
+
136
+ const onPasswordChangeError = useCallback ( ( err : Error ) : void => {
101
137
logger . error ( "Failed to change password: " + err ) ;
102
138
103
139
let underlyingError = err ;
@@ -127,85 +163,49 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
127
163
title : _t ( "settings|general|error_password_change_title" ) ,
128
164
description : errorMessageToDisplay ,
129
165
} ) ;
130
- } ;
166
+ } , [ ] ) ;
131
167
132
- private onPasswordChanged = ( ) : void => {
168
+ const onPasswordChanged = useCallback ( ( ) : void => {
133
169
const description = _t ( "settings|general|password_change_success" ) ;
134
170
// TODO: Figure out a design that doesn't involve replacing the current dialog
135
171
Modal . createDialog ( ErrorDialog , {
136
172
title : _t ( "common|success" ) ,
137
173
description,
138
174
} ) ;
139
- } ;
175
+ } , [ ] ) ;
140
176
141
- private onDeactivateClicked = ( ) : void => {
177
+ const onDeactivateClicked = useCallback ( ( ) : void => {
142
178
Modal . createDialog ( DeactivateAccountDialog , {
143
179
onFinished : ( success ) => {
144
- if ( success ) this . props . closeSettingsFn ( ) ;
180
+ if ( success ) closeSettingsFn ( ) ;
145
181
} ,
146
182
} ) ;
147
- } ;
148
-
149
- private renderAccountSection ( ) : JSX . Element | undefined {
150
- if ( ! this . state . canChangePassword ) return undefined ;
151
-
152
- return (
153
- < >
154
- < SettingsSubsection
155
- heading = { _t ( "settings|general|account_section" ) }
156
- stretchContent
157
- data-testid = "accountSection"
158
- >
159
- < SettingsSubsectionText > { _t ( "settings|general|password_change_section" ) } </ SettingsSubsectionText >
160
- < ChangePassword
161
- className = "mx_GeneralUserSettingsTab_section--account_changePassword"
162
- rowClassName = ""
163
- buttonKind = "primary"
164
- onError = { this . onPasswordChangeError }
165
- onFinished = { this . onPasswordChanged }
166
- />
167
- </ SettingsSubsection >
168
- </ >
169
- ) ;
170
- }
183
+ } , [ closeSettingsFn ] ) ;
171
184
172
- private renderManagementSection ( ) : JSX . Element {
173
- // TODO: Improve warning text for account deactivation
174
- return (
175
- < SettingsSection heading = { _t ( "settings|general|deactivate_section" ) } >
176
- < SettingsSubsection
177
- heading = { _t ( "settings|general|account_management_section" ) }
178
- data-testid = "account-management-section"
179
- description = { _t ( "settings|general|deactivate_warning" ) }
180
- >
181
- < AccessibleButton onClick = { this . onDeactivateClicked } kind = "danger" >
182
- { _t ( "settings|general|deactivate_section" ) }
183
- </ AccessibleButton >
184
- </ SettingsSubsection >
185
- </ SettingsSection >
186
- ) ;
185
+ let accountManagementSection : JSX . Element | undefined ;
186
+ const isAccountManagedExternally = Boolean ( externalAccountManagementUrl ) ;
187
+ if ( SettingsStore . getValue ( UIFeature . Deactivate ) && ! isAccountManagedExternally ) {
188
+ accountManagementSection = < ManagementSection onDeactivateClicked = { onDeactivateClicked } /> ;
187
189
}
188
190
189
- public render ( ) : React . ReactNode {
190
- let accountManagementSection : JSX . Element | undefined ;
191
- const isAccountManagedExternally = ! ! this . state . externalAccountManagementUrl ;
192
- if ( SettingsStore . getValue ( UIFeature . Deactivate ) && ! isAccountManagedExternally ) {
193
- accountManagementSection = this . renderManagementSection ( ) ;
194
- }
191
+ return (
192
+ < SettingsTab data-testid = "mx_GeneralUserSettingsTab" >
193
+ < SettingsSection >
194
+ < UserProfileSettings
195
+ externalAccountManagementUrl = { externalAccountManagementUrl }
196
+ canSetDisplayName = { canSetDisplayName }
197
+ canSetAvatar = { canSetAvatar }
198
+ />
199
+ < UserPersonalInfoSettings canMake3pidChanges = { canMake3pidChanges } />
200
+ < AccountSection
201
+ canChangePassword = { canChangePassword }
202
+ onPasswordChanged = { onPasswordChanged }
203
+ onPasswordChangeError = { onPasswordChangeError }
204
+ />
205
+ </ SettingsSection >
206
+ { accountManagementSection }
207
+ </ SettingsTab >
208
+ ) ;
209
+ } ;
195
210
196
- return (
197
- < SettingsTab data-testid = "mx_GeneralUserSettingsTab" >
198
- < SettingsSection >
199
- < UserProfileSettings
200
- externalAccountManagementUrl = { this . state . externalAccountManagementUrl }
201
- canSetDisplayName = { this . state . canSetDisplayName }
202
- canSetAvatar = { this . state . canSetAvatar }
203
- />
204
- < UserPersonalInfoSettings canMake3pidChanges = { this . state . canMake3pidChanges } />
205
- { this . renderAccountSection ( ) }
206
- </ SettingsSection >
207
- { accountManagementSection }
208
- </ SettingsTab >
209
- ) ;
210
- }
211
- }
211
+ export default GeneralUserSettingsTab ;
0 commit comments