forked from baron/baron-sso
feat: add env-aware client log policy and const lint fixes
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
import '../../../../core/notifiers/auth_notifier.dart';
|
||||
import '../../../../core/i18n/locale_utils.dart';
|
||||
@@ -22,6 +23,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
static const _surface = Colors.white;
|
||||
static const _border = Color(0xFFE5E7EB);
|
||||
static const _subtle = Color(0xFFF7F8FA);
|
||||
static final _log = Logger('ProfilePage');
|
||||
|
||||
UserProfile? _cachedProfile;
|
||||
String? _editingField;
|
||||
@@ -65,6 +67,22 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_phoneCodeFocus.addListener(_onPhoneCodeFocusChange);
|
||||
}
|
||||
|
||||
void _debugLog(
|
||||
String event, {
|
||||
String? field,
|
||||
String? reason,
|
||||
bool? changed,
|
||||
bool? hasFocus,
|
||||
}) {
|
||||
final parts = <String>['event=$event'];
|
||||
if (field != null) parts.add('field=$field');
|
||||
if (reason != null) parts.add('reason=$reason');
|
||||
if (changed != null) parts.add('changed=$changed');
|
||||
if (hasFocus != null) parts.add('hasFocus=$hasFocus');
|
||||
if (_editingField != null) parts.add('editing=$_editingField');
|
||||
_log.fine(parts.join(' '));
|
||||
}
|
||||
|
||||
void _onNameFocusChange() {
|
||||
if (!mounted) return;
|
||||
if (!_nameFocus.hasFocus && _nameTouched) {
|
||||
@@ -77,6 +95,11 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
|
||||
void _onDepartmentFocusChange() {
|
||||
if (!mounted) return;
|
||||
_debugLog(
|
||||
'department_focus_change',
|
||||
field: 'department',
|
||||
hasFocus: _departmentFocus.hasFocus,
|
||||
);
|
||||
if (!_departmentFocus.hasFocus && _departmentTouched) {
|
||||
final profile = ref.read(profileProvider).value ?? _cachedProfile;
|
||||
if (profile != null) _autoSaveIfEditing(profile, 'department');
|
||||
@@ -180,6 +203,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
}
|
||||
|
||||
void _startEditing(String field, UserProfile profile) {
|
||||
_debugLog('start_editing', field: field);
|
||||
setState(() {
|
||||
_editingField = field;
|
||||
if (field == 'name') {
|
||||
@@ -356,12 +380,25 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
void _autoSaveIfEditing(UserProfile profile, String field) {
|
||||
if (_editingField != field) return;
|
||||
if (_skipAutoSaveField == field) {
|
||||
_debugLog('autosave_skip', field: field, reason: 'skip_flag');
|
||||
_skipAutoSaveField = null;
|
||||
return;
|
||||
}
|
||||
if (_isVerifying) return;
|
||||
if (_isSavingField) return;
|
||||
if (_isVerifying) {
|
||||
_debugLog('autosave_skip', field: field, reason: 'verifying');
|
||||
return;
|
||||
}
|
||||
if (_isSavingField) {
|
||||
_debugLog('autosave_skip', field: field, reason: 'saving_in_flight');
|
||||
return;
|
||||
}
|
||||
if (!_hasFieldChanged(profile, field)) {
|
||||
_debugLog(
|
||||
'autosave_skip',
|
||||
field: field,
|
||||
reason: 'unchanged',
|
||||
changed: false,
|
||||
);
|
||||
setState(() {
|
||||
if (field == 'phone') {
|
||||
_resetPhoneState();
|
||||
@@ -375,6 +412,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
_debugLog('autosave_trigger', field: field, changed: true);
|
||||
_saveField(profile);
|
||||
}
|
||||
|
||||
@@ -412,25 +450,33 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
|
||||
Future<void> _saveField(UserProfile profile) async {
|
||||
if (_editingField == null) return;
|
||||
if (_isSavingField) return;
|
||||
if (_isSavingField) {
|
||||
_debugLog('save_skip', reason: 'saving_in_flight');
|
||||
return;
|
||||
}
|
||||
final currentField = _editingField!;
|
||||
|
||||
final nextName = _editingField == 'name'
|
||||
final nextName = currentField == 'name'
|
||||
? _nameController!.text.trim()
|
||||
: profile.name;
|
||||
final nextPhone = _editingField == 'phone'
|
||||
final nextPhone = currentField == 'phone'
|
||||
? _phoneController!.text.trim()
|
||||
: profile.phone;
|
||||
final nextDepartment = _editingField == 'department'
|
||||
final nextDepartment = currentField == 'department'
|
||||
? _departmentController!.text.trim()
|
||||
: profile.department;
|
||||
|
||||
if (_editingField == 'name' && nextName.isEmpty) {
|
||||
_debugLog('save_attempt', field: currentField);
|
||||
|
||||
if (currentField == 'name' && nextName.isEmpty) {
|
||||
_debugLog('save_skip', field: currentField, reason: 'empty_name');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(tr('msg.userfront.profile.name_required'))),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_editingField == 'department' && nextDepartment.isEmpty) {
|
||||
if (currentField == 'department' && nextDepartment.isEmpty) {
|
||||
_debugLog('save_skip', field: currentField, reason: 'empty_department');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(tr('msg.userfront.profile.department_required')),
|
||||
@@ -438,14 +484,20 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_editingField == 'phone') {
|
||||
if (currentField == 'phone') {
|
||||
if (nextPhone.isEmpty) {
|
||||
_debugLog('save_skip', field: currentField, reason: 'empty_phone');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(tr('msg.userfront.profile.phone_required'))),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_isPhoneChanged && !_isPhoneVerified) {
|
||||
_debugLog(
|
||||
'save_skip',
|
||||
field: currentField,
|
||||
reason: 'phone_not_verified',
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(tr('msg.userfront.profile.phone_verify_required')),
|
||||
@@ -455,7 +507,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!_hasFieldChanged(profile, _editingField!)) {
|
||||
if (!_hasFieldChanged(profile, currentField)) {
|
||||
_debugLog(
|
||||
'save_skip',
|
||||
field: currentField,
|
||||
reason: 'unchanged',
|
||||
changed: false,
|
||||
);
|
||||
setState(() {
|
||||
if (_editingField == 'phone') {
|
||||
_resetPhoneState();
|
||||
@@ -468,6 +526,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
}
|
||||
|
||||
_isSavingField = true;
|
||||
_debugLog('save_dispatch', field: currentField, changed: true);
|
||||
|
||||
try {
|
||||
await ref
|
||||
@@ -479,7 +538,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
if (_editingField == 'phone') {
|
||||
if (currentField == 'phone') {
|
||||
_initialPhone = nextPhone;
|
||||
_resetPhoneState();
|
||||
}
|
||||
@@ -487,11 +546,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
_nameTouched = false;
|
||||
_departmentTouched = false;
|
||||
});
|
||||
_debugLog('save_success', field: currentField);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(tr('msg.userfront.profile.update_success'))),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
_debugLog('save_failed', field: currentField, reason: e.toString());
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
||||
Reference in New Issue
Block a user