Fix avatar and display name update issues
- Remove avatar handling from profile update to prevent overwriting DB with display URL - Skip ensureParent for .avatars to speed up upload - Add change detection for display name save button - Update API client to not send avatarUrl in profile update
This commit is contained in:
@@ -185,11 +185,9 @@ class ApiClient {
|
||||
Future<Map<String, dynamic>> updateUserProfile({
|
||||
required String displayName,
|
||||
String? email,
|
||||
String? avatarUrl,
|
||||
}) async {
|
||||
final data = <String, dynamic>{'displayName': displayName};
|
||||
if (email != null) data['email'] = email;
|
||||
if (avatarUrl != null) data['avatarUrl'] = avatarUrl;
|
||||
|
||||
return putRaw('/user/profile', data: data);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
bool _isUploadingAvatar = false;
|
||||
double _avatarUploadProgress = 0.0;
|
||||
String? _error;
|
||||
bool _hasChanges = false;
|
||||
|
||||
// Profile fields
|
||||
late TextEditingController _displayNameController;
|
||||
@@ -40,6 +41,11 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_displayNameController = TextEditingController();
|
||||
_displayNameController.addListener(() {
|
||||
if (mounted && _currentUser != null) {
|
||||
setState(() => _hasChanges = _displayNameController.text != (_currentUser!.displayName ?? ''));
|
||||
}
|
||||
});
|
||||
_currentPasswordController = TextEditingController();
|
||||
_newPasswordController = TextEditingController();
|
||||
_confirmPasswordController = TextEditingController();
|
||||
@@ -52,6 +58,7 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
_currentUser = authState.user;
|
||||
_displayNameController.text = _currentUser?.displayName ?? '';
|
||||
_avatarUrl = _currentUser?.avatarUrl;
|
||||
_hasChanges = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -145,6 +152,11 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
void _onSavePressed() {
|
||||
if (!_hasChanges || _isLoading) return;
|
||||
_updateProfile();
|
||||
}
|
||||
|
||||
Future<void> _updateProfile() async {
|
||||
if (_currentUser == null) {
|
||||
return;
|
||||
@@ -155,7 +167,6 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
final apiClient = GetIt.I<ApiClient>();
|
||||
await apiClient.updateUserProfile(
|
||||
displayName: _displayNameController.text,
|
||||
avatarUrl: _avatarUrl,
|
||||
);
|
||||
|
||||
final updatedUser = _currentUser!.copyWith(
|
||||
@@ -641,7 +652,7 @@ class _AccountSettingsDialogState extends State<AccountSettingsDialog> {
|
||||
child: SizedBox(
|
||||
width: 144,
|
||||
child: ModernGlassButton(
|
||||
onPressed: () => _updateProfile(),
|
||||
onPressed: _onSavePressed,
|
||||
isLoading: _isLoading,
|
||||
child: _isLoading
|
||||
? const SizedBox(
|
||||
|
||||
@@ -3872,7 +3872,6 @@ func updateUserProfileHandler(w http.ResponseWriter, r *http.Request, db *databa
|
||||
var req struct {
|
||||
DisplayName *string `json:"displayName"`
|
||||
Email *string `json:"email"`
|
||||
AvatarURL *string `json:"avatarUrl"`
|
||||
}
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
@@ -3895,11 +3894,6 @@ func updateUserProfileHandler(w http.ResponseWriter, r *http.Request, db *databa
|
||||
args = append(args, *req.Email)
|
||||
argIndex++
|
||||
}
|
||||
if req.AvatarURL != nil {
|
||||
setParts = append(setParts, fmt.Sprintf("avatar_url = $%d", argIndex))
|
||||
args = append(args, *req.AvatarURL)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if len(setParts) == 0 {
|
||||
// No fields to update
|
||||
@@ -3927,9 +3921,6 @@ func updateUserProfileHandler(w http.ResponseWriter, r *http.Request, db *databa
|
||||
if req.Email != nil {
|
||||
metadata["email"] = *req.Email
|
||||
}
|
||||
if req.AvatarURL != nil {
|
||||
metadata["avatarUrl"] = *req.AvatarURL
|
||||
}
|
||||
|
||||
auditLogger.Log(r.Context(), audit.Entry{
|
||||
UserID: &userID,
|
||||
|
||||
@@ -91,9 +91,11 @@ func (c *WebDAVClient) Upload(ctx context.Context, remotePath string, r io.Reade
|
||||
if c == nil {
|
||||
return fmt.Errorf("no webdav client configured")
|
||||
}
|
||||
// Ensure parent collections
|
||||
if err := c.ensureParent(ctx, remotePath); err != nil {
|
||||
return err
|
||||
// Ensure parent collections, skip for .avatars as it should exist
|
||||
if !strings.HasPrefix(remotePath, ".avatars/") {
|
||||
if err := c.ensureParent(ctx, remotePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Construct URL
|
||||
// remotePath might be like /orgs/<id>/file.txt; ensure it joins to basePrefix
|
||||
|
||||
Reference in New Issue
Block a user