forked from baron/baron-sso
userfront gateway 분리.
This commit is contained in:
@@ -31,6 +31,11 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
final FocusNode _departmentFocus = FocusNode();
|
||||
final FocusNode _phoneFocus = FocusNode();
|
||||
final FocusNode _phoneCodeFocus = FocusNode();
|
||||
bool _nameTouched = false;
|
||||
bool _departmentTouched = false;
|
||||
bool _phoneTouched = false;
|
||||
bool _phoneCodeTouched = false;
|
||||
bool _isSavingField = false;
|
||||
|
||||
String _initialPhone = '';
|
||||
bool _isPhoneChanged = false;
|
||||
@@ -102,6 +107,8 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_isCodeSent = false;
|
||||
_isVerifying = false;
|
||||
_codeController?.clear();
|
||||
_phoneTouched = false;
|
||||
_phoneCodeTouched = false;
|
||||
}
|
||||
|
||||
void _startEditing(String field, UserProfile profile) {
|
||||
@@ -117,6 +124,16 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_resetPhoneState();
|
||||
}
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
if (field == 'name') {
|
||||
FocusScope.of(context).requestFocus(_nameFocus);
|
||||
} else if (field == 'department') {
|
||||
FocusScope.of(context).requestFocus(_departmentFocus);
|
||||
} else if (field == 'phone') {
|
||||
FocusScope.of(context).requestFocus(_phoneFocus);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelEditing(UserProfile profile) {
|
||||
@@ -131,6 +148,8 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_resetPhoneState();
|
||||
}
|
||||
_editingField = null;
|
||||
_nameTouched = false;
|
||||
_departmentTouched = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -193,18 +212,55 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
void _autoSaveIfEditing(UserProfile profile, String field) {
|
||||
if (_editingField != field) return;
|
||||
if (_isVerifying) return;
|
||||
if (_isSavingField) return;
|
||||
if (!_hasFieldChanged(profile, field)) {
|
||||
setState(() {
|
||||
if (field == 'phone') {
|
||||
_resetPhoneState();
|
||||
}
|
||||
_editingField = null;
|
||||
if (field == 'name') {
|
||||
_nameTouched = false;
|
||||
} else if (field == 'department') {
|
||||
_departmentTouched = false;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
_saveField(profile);
|
||||
}
|
||||
|
||||
void _handlePhoneFocusChange(UserProfile profile) {
|
||||
if (_editingField != 'phone') return;
|
||||
if (_isVerifying) return;
|
||||
if (_isSavingField) return;
|
||||
if (_phoneFocus.hasFocus || _phoneCodeFocus.hasFocus) return;
|
||||
if (!_hasFieldChanged(profile, 'phone')) {
|
||||
setState(() {
|
||||
_resetPhoneState();
|
||||
_editingField = null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
_saveField(profile);
|
||||
}
|
||||
|
||||
bool _hasFieldChanged(UserProfile profile, String field) {
|
||||
if (field == 'name') {
|
||||
return (_nameController?.text.trim() ?? '') != profile.name;
|
||||
}
|
||||
if (field == 'department') {
|
||||
return (_departmentController?.text.trim() ?? '') != profile.department;
|
||||
}
|
||||
if (field == 'phone') {
|
||||
return (_phoneController?.text.trim() ?? '') != profile.phone;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> _saveField(UserProfile profile) async {
|
||||
if (_editingField == null) return;
|
||||
if (_isSavingField) return;
|
||||
|
||||
final nextName = _editingField == 'name'
|
||||
? _nameController!.text.trim()
|
||||
@@ -243,6 +299,20 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!_hasFieldChanged(profile, _editingField!)) {
|
||||
setState(() {
|
||||
if (_editingField == 'phone') {
|
||||
_resetPhoneState();
|
||||
}
|
||||
_editingField = null;
|
||||
_nameTouched = false;
|
||||
_departmentTouched = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
_isSavingField = true;
|
||||
|
||||
try {
|
||||
await ref.read(profileProvider.notifier).updateProfile(
|
||||
name: nextName,
|
||||
@@ -256,6 +326,8 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_resetPhoneState();
|
||||
}
|
||||
_editingField = null;
|
||||
_nameTouched = false;
|
||||
_departmentTouched = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('정보가 수정되었습니다.')),
|
||||
@@ -267,6 +339,8 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
SnackBar(content: Text('수정 실패: $e')),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
_isSavingField = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,23 +528,53 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 8),
|
||||
Focus(
|
||||
focusNode: field == 'name' ? _nameFocus : _departmentFocus,
|
||||
onFocusChange: (hasFocus) {
|
||||
if (!hasFocus) {
|
||||
_autoSaveIfEditing(profile, field);
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
focusNode: field == 'name' ? _nameFocus : _departmentFocus,
|
||||
textInputAction: TextInputAction.done,
|
||||
onSubmitted: (_) => _autoSaveIfEditing(profile, field),
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: label,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Focus(
|
||||
focusNode: field == 'name' ? _nameFocus : _departmentFocus,
|
||||
onFocusChange: (hasFocus) {
|
||||
if (field == 'name') {
|
||||
if (hasFocus) {
|
||||
_nameTouched = true;
|
||||
return;
|
||||
}
|
||||
if (!_nameTouched) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (field == 'department') {
|
||||
if (hasFocus) {
|
||||
_departmentTouched = true;
|
||||
return;
|
||||
}
|
||||
if (!_departmentTouched) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!hasFocus) {
|
||||
_autoSaveIfEditing(profile, field);
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
focusNode: field == 'name' ? _nameFocus : _departmentFocus,
|
||||
textInputAction: TextInputAction.done,
|
||||
onSubmitted: (_) => _autoSaveIfEditing(profile, field),
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: label,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
OutlinedButton(
|
||||
onPressed: isUpdating ? null : () => _cancelEditing(profile),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -504,6 +608,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
child: Focus(
|
||||
focusNode: _phoneFocus,
|
||||
onFocusChange: (hasFocus) {
|
||||
if (hasFocus) {
|
||||
_phoneTouched = true;
|
||||
return;
|
||||
}
|
||||
if (!_phoneTouched) {
|
||||
return;
|
||||
}
|
||||
if (!hasFocus) {
|
||||
_handlePhoneFocusChange(profile);
|
||||
}
|
||||
@@ -531,6 +642,11 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
onPressed: _isVerifying ? null : _sendCode,
|
||||
child: Text(_isCodeSent ? '재전송' : '인증요청'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton(
|
||||
onPressed: isUpdating ? null : () => _cancelEditing(profile),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_isCodeSent && !_isPhoneVerified) ...[
|
||||
@@ -542,6 +658,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
child: Focus(
|
||||
focusNode: _phoneCodeFocus,
|
||||
onFocusChange: (hasFocus) {
|
||||
if (hasFocus) {
|
||||
_phoneCodeTouched = true;
|
||||
return;
|
||||
}
|
||||
if (!_phoneCodeTouched) {
|
||||
return;
|
||||
}
|
||||
if (!hasFocus) {
|
||||
_handlePhoneFocusChange(profile);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user