Skip to content

feat: add CraftDImage component for Android/KMP#103

Merged
rviannaoliveira merged 11 commits intomainfrom
feature/add-craftd-image-clean
Apr 24, 2026
Merged

feat: add CraftDImage component for Android/KMP#103
rviannaoliveira merged 11 commits intomainfrom
feature/add-craftd-image-clean

Conversation

@rviannaoliveira
Copy link
Copy Markdown
Contributor

Summary

  • Adds CraftDImage component to craftd-core, craftd-compose and craftd-xml
  • ImageProperties model com url, contentScale, contentDescription e actionProperties
  • CraftDContentScale enum mapeando para Compose ContentScale
  • CraftDImageBuilder (Compose) com imageLoader injetável — não pré-registrado por design
  • CraftDImageComponentRender (XML) com imageLoader injetável — registrado em CraftDBuilderManager
  • Testes unitários para serialização de ImageProperties, toContentScale() e key do CraftDImageBuilder
  • Docs atualizados: compose.md e view-system.md
  • Sample app com Coil (Compose) e Picasso (XML)
  • Otimizações de contexto: instructions por plataforma em .claude/instructions/, regra de platform: no /propose

Test plan

  • ./gradlew :craftd-core:testDebugUnitTest passes
  • ./gradlew :craftd-compose:testDebugUnitTest passes
  • ./gradlew :app-sample-android:assembleDebug passes
  • CraftDImage renderiza na tela Compose do sample
  • CraftDImage renderiza na tela XML do sample
  • Tap na imagem dispara action (deeplink/analytics)

🤖 Generated with Claude Code

rviannaoliveira and others added 5 commits April 11, 2026 20:09
- Add IMAGE_COMPONENT to CraftDComponentKey enum
- Add CraftDContentScale enum (CROP, FIT, FILL_BOUNDS, FILL_WIDTH, FILL_HEIGHT, INSIDE, NONE)
- Add ImageProperties data class with url, contentScale, contentDescription, actionProperties

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Compose:
- Add toContentScale() extension mapping CraftDContentScale → ContentScale
- Add CraftDImage composable with injectable imageLoader lambda
- Add CraftDImageBuilder with injectable imageLoader constructor param

XML:
- Add CraftDImageComponent (AppCompatImageView wrapper)
- Add CraftDImageComponentRender with injectable imageLoader
- Register CraftDImageComponentRender in CraftDBuilderManager via optional imageLoader param

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Unit tests for ImageProperties serialization, toContentScale() and CraftDImageBuilder key
- Updated docs/how-to-use/compose.md and view-system.md with CraftDImage usage examples
- Registered CraftDImageBuilder (Coil) in Compose sample and CraftDImageComponentRender (Picasso) in XML sample
- Added CraftDImage entry to dynamic.json
- Switched app-sample-android to local craftd-xml project dependency
- Added Coil 2.6.0 to libs.versions.toml
- Deleted notes.md after context consumed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

@rviannaoliveira rviannaoliveira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — feat: add CraftDImage component for Android/KMP

Resumo

A implementação está bem estruturada — abstração do loader injetável, separação correta entre módulos, testes e docs presentes. Mas há dois bugs e três problemas que precisam de atenção antes do merge.


Crítico

1. contentScale ignorado silenciosamente

A propriedade contentScale de ImageProperties nunca é usada. A função toContentScale() foi criada mas não é chamada em lugar nenhum.

  • CraftDImage.kt: o imageLoader recebe (url, contentDescription, modifier) — sem ContentScale
  • CraftDImageComponentRender: não mapeia contentScale para ImageView.scaleType
  • O sample app passa AsyncImage sem contentScale

O resultado é que o servidor pode enviar "contentScale": "CROP" e o cliente ignora completamente. A assinatura do imageLoader precisa incluir ContentScale (Compose) ou um parâmetro equivalente.

2. @Stable/@Immutable de androidx.compose.runtime em commonMain — viola regra 4

CraftDContentScale e ImageProperties em craftd-core/commonMain importam androidx.compose.runtime.Stable e androidx.compose.runtime.Immutable. Essas são anotações de plataforma Compose, proibidas em commonMain.

Solução: remover as anotações (são hints de compilador Compose, desnecessárias em código de modelo de um módulo core) ou mover via expect/actual se o impacto de performance realmente justificar.


Moderado

3. Builder Compose não registrado — regra 9

A regra 9 do CLAUDE.md diz que todo novo builder deve ser registrado no CraftDBuilderManager. A justificativa de design (requer imageLoader) é válida, mas diverge da regra sem documentação explícita.

Sugestão: atualizar o CLAUDE.md para documentar essa exceção como padrão ("builders com dependências externas obrigatórias não são pré-registrados"), ou fornecer um imageLoader default no construtor que lance IllegalStateException com mensagem clara.

4. Testes de CraftDImageBuilderTest incompletos — task 4.3

A task 4.3 pedia: "verify imageLoader is called with correct args and actionProperties triggers listener". O teste atual só verifica a key. Falta:

  • Verificar que o imageLoader é invocado com url e contentDescription corretos
  • Verificar que listener.invoke(actionProperties) é chamado quando actionProperties != null

Também não há testes para CraftDImageComponentRender (XML).


Positivo

  • Abstração via lambda injetável está correta — cumpre regra 10 (sem acoplamento a Coil/Picasso)
  • onAction/fallback coberto tanto no Compose quanto no XML
  • ImageProperties no craftd-core com serialização @Serializable correta
  • Docs de compose.md e view-system.md atualizados com exemplos práticos
  • XML: registro condicional (imageLoader?.let { ... }) é boa solução para o CraftDBuilderManager
  • Sample app demonstra ambas as plataformas (Coil no Compose, Picasso no XML)
  • Instructions por plataforma em .claude/instructions/ são uma melhoria valiosa ao CLAUDE.md

Ação necessária antes do merge

  1. Corrigir a assinatura do imageLoader para incluir ContentScale e efetivamente usar properties.contentScale
  2. Remover @Stable/@Immutable de commonMain (CraftDContentScale e ImageProperties)
  3. Complementar os testes do CraftDImageBuilderTest (imageLoader chamado + listener disparado)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 11, 2026

🦙 MegaLinter status: ⚠️ WARNING

Descriptor Linter Files Fixed Errors Warnings Elapsed time
⚠️ KOTLIN detekt yes 191 no 5.4s
⚠️ MARKDOWN markdown-table-formatter 46 1 0 0.3s
⚠️ YAML prettier 18 1 4 0.88s

See detailed report in MegaLinter reports

You could have the same capabilities but better runtime performances if you use a MegaLinter flavor:

MegaLinter is graciously provided by OX Security

Comment thread android_kmp/app-sample-android/build.gradle.kts
Comment thread android_kmp/craftd-core/build.gradle.kts
Comment thread android_kmp/gradle/libs.versions.toml
Copy link
Copy Markdown
Collaborator

@gabrielbmoro gabrielbmoro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remover o coil e permitir que o dev use a biblioteca de image loading que quiser

rviannaoliveira and others added 3 commits April 13, 2026 14:53
Merge changes from main (unit tests, build-logic updates) into the feature branch.
Resolved conflicts in CraftDComponentKeyTest (IMAGE_COMPONENT count) and
CraftDContentScale (removed Compose annotations from commonMain).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ns from commonMain

- Add ContentScale param to imageLoader lambda in CraftDImage and CraftDImageBuilder
  so the server-driven contentScale is actually applied when loading images
- Map CraftDContentScale to ImageView.ScaleType in CraftDImageComponentRender (XML)
- Remove @Stable/@immutable from ImageProperties (androidx.compose deps in commonMain)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Archive empty add-craftd-image change (2026-04-22)
- Update tasks.md in 2026-04-13-add-craftd-image-android-kmp:
  - 1.4: remove @Stable/@immutable from ImageProperties (commonMain violation)
  - 2.2a: imageLoader lambda now includes ContentScale param
  - 3.2a: contentScale mapped to ImageView.scaleType in XML render
  - 4.3: correct description of what tests actually cover
  - 4.4: pending tests task for next propose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Register CraftDImageBuilder with Coil 3 (KMP-compatible) AsyncImage in the
CMP sample, mirroring the android sample setup. Adds coil3-compose to
commonMain and coil3-network-okhttp to androidMain for network image loading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rviannaoliveira rviannaoliveira merged commit 859ff4d into main Apr 24, 2026
4 checks passed
@rviannaoliveira rviannaoliveira deleted the feature/add-craftd-image-clean branch April 24, 2026 11:19
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.

2 participants