fix(contacts): adversarial-review fixes — delete UB, JPEG decode, footer clip

From an adversarial review of the avatar edit-dialog + image picker:

- HIGH: the per-row Delete icon called doDelete() (which erases from
  book.entries()) INSIDE the loop iterating that same vector — out-of-bounds
  reads / wrong rows on the confirming click. Defer it until after the loop.
- HIGH: the decode stack was compiled PNG-only (STBI_ONLY_PNG) while the image
  picker accepts .jpg/.jpeg/.bmp/.gif, so picking a JPEG (the common photo
  case) silently produced a non-loading avatar + an orphaned copy on disk.
  Enable JPEG/BMP/GIF decoders, and guard the pick: verify the source decodes
  before copying/committing (new contact_avatar_bad_image string, 8 langs).
- MED: the fixed, non-scrolling edit card floored bodyH at 260*dp, which could
  push Save/Cancel below the card on short windows — floor lowered so the
  footer always stays inside.
- LOW: the live-preview panel rounding is now dp-scaled (10*dp) to match the
  real list card it mirrors.
- LOW: re-picking the same source path after its contents changed showed a
  stale cached texture — evict the avatar texture cache entry on re-pick.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 09:49:34 -05:00
parent e1df5ea798
commit 9649654c3c
11 changed files with 43 additions and 5 deletions

View File

@@ -1243,6 +1243,7 @@ void I18n::loadBuiltinEnglish()
strings_["contact_avatar_remove"] = "Remove";
strings_["contact_avatar_image_hint"] = "The image is copied into the app so it stays available if the original moves.";
strings_["contact_avatar_copy_failed"] = "Could not copy that image.";
strings_["contact_avatar_bad_image"] = "That image couldn't be loaded.";
strings_["contact_global_badge_tt"] = "Global contact — visible in every wallet";
strings_["address_book_added"] = "Address added to book";
strings_["address_book_count"] = "%zu addresses saved";

View File

@@ -7,7 +7,12 @@
// stb_image — single-file image loader (public domain)
// Only compiled here; all other files just include the header.
#define STB_IMAGE_IMPLEMENTATION
// Formats we actually decode: PNG for app assets, plus JPEG/BMP/GIF so user-chosen contact avatars
// (which are commonly JPEGs) load — must stay in sync with ImagePicker's accepted extensions.
#define STBI_ONLY_PNG
#define STBI_ONLY_JPEG
#define STBI_ONLY_BMP
#define STBI_ONLY_GIF
#define STBI_NO_STDIO // we do our own fread for portability
#include "stb_image.h"