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