Skip to content

Build out profile modal#6

Merged
GodPuffin merged 2 commits into
mainfrom
feat/profile-modal-avatar
Apr 7, 2026
Merged

Build out profile modal#6
GodPuffin merged 2 commits into
mainfrom
feat/profile-modal-avatar

Conversation

@GodPuffin
Copy link
Copy Markdown
Owner

Summary

  • add profile picture upload/display to the profile modal and profile page
  • show the username alongside the editable display name
  • persist avatars through PocketBase's existing user avatar file field and expose avatar URLs through session view data

Tests

  • $env:GOCACHE = (Join-Path (Get-Location) 'tmp\go-build'); go test ./...

@GodPuffin
Copy link
Copy Markdown
Owner Author

@claude review

@claude
Copy link
Copy Markdown

claude Bot commented Apr 7, 2026

Claude finished @GodPuffin's task in 1m 52s —— View job


Code Review

  • Gather context and understand request
  • Read changed files
  • Analyze code for bugs, security issues, and best practices
  • Post review feedback

Overall this is a clean, well-scoped PR. The avatar feature is correctly wired through PocketBase's file API, the new userprofile package is nicely isolated, and tests are present. A few issues worth addressing:


Issues

1. Stale avatarURL returned in JSON response after upload

profile.go:129 — after form.Submit(), the code calls userprofile.AvatarURL(authRecord) to return the new avatar URL in the JSON response. PocketBase does update the record in-place after Submit(), but only with the new filename it generated — the in-memory authRecord object is the same pointer that was loaded before the form was submitted. Confirm that PocketBase's RecordUpsert.Submit() actually mutates authRecord's avatar field with the new filename. If it doesn't, the response JSON will carry the old avatar URL, causing the frontend to display the stale image until the next hard reload (which the hyperscript triggers, so the user won't notice — but it's still incorrect data in the API response).

Fix this →


2. No server-side file type or size validation

profile.go:100-114 — the file input in templates restricts types via the accept attribute and the helper text says "up to 5 MB", but neither is enforced server-side. Any file type and any size can be uploaded. This relies entirely on whatever PocketBase schema constraints are configured for the avatar field.

If the avatar field in PocketBase has maxSize and mimeTypes set in the collection schema, PocketBase will reject invalid files during form.Submit() and the generic error message "Failed to update profile" will be returned — which is functional but gives the user no feedback about why it failed. Consider catching and surfacing the PocketBase validation error message here.

Fix this →


3. slice template helper is byte-indexed, not rune-indexed

view.go:26-34 — the slice helper slices strings by byte offset, not by Unicode code point. This is a pre-existing issue, but this PR introduces several new uses of {{slice .name 0 1 | upper}} and {{slice .Name 0 1 | upper}} in layout.html (lines 43, 154) and plan_details.html (line 269). For any display name or username that starts with a multibyte UTF-8 character (e.g. "Ñoño", "张伟"), this will produce a broken/garbled character in the avatar placeholder.

// Current (byte slice)
return s[i:j]

// Fix: use rune slice
runes := []rune(s)
if i >= len(runes) { return "" }
if j > len(runes) { j = len(runes) }
return string(runes[i:j])

Fix this →


Minor Notes

  • profile.go:99 — the multipart content-type check strings.HasPrefix(c.Request().Header.Get(echo.HeaderContentType), "multipart/form-data") is fine, but since the form always sets enctype="multipart/form-data", this branch will always be taken for normal browser submissions. The check isn't harmful but could be simplified to just always attempt to read the avatar file.

  • avatar_test.go — good coverage of the nil/empty cases. One gap: no test for a record whose Collection() returns nil (the guard at line 11 covers record.Collection() == nil, but only AvatarURL(nil) is tested for the nil path). Not a blocker, just a note.

  • layout.html modal + profile.html page — both forms POST to /profile. This duplication is pragmatic but means any future changes to the profile form need to be made in two places. Worth a comment or extracting to a partial template long-term.


Summary

Core logic is solid. The main actionable items are: (1) verify/fix the post-submit authRecord avatar URL accuracy, (2) surface PocketBase validation errors to the user for better UX, and (3) fix the byte-slice issue in the slice template helper, which was pre-existing but is now used in more places.
|

@GodPuffin GodPuffin merged commit fd24e42 into main Apr 7, 2026
2 checks passed
@GodPuffin GodPuffin deleted the feat/profile-modal-avatar branch April 7, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant