Skip to content

Fix IME composition loss and reply prefix overwrite#120

Merged
malkoG merged 1 commit intohackers-pub:mainfrom
dalinaum:fix/ime-composition-loss
Apr 17, 2026
Merged

Fix IME composition loss and reply prefix overwrite#120
malkoG merged 1 commit intohackers-pub:mainfrom
dalinaum:fix/ime-composition-loss

Conversation

@dalinaum
Copy link
Copy Markdown
Contributor

Summary

  • ComposeScreen.kt: TextFieldValue() 생성자 대신 copy() 사용하여 IME composition 상태 보존
  • ComposeViewModel.kt: setReplyTarget()이 비동기 응답 후 사용자가 이미 입력한 content를 덮어쓰지 않도록 방어
  • ComposeViewModelTest.kt: setReplyTarget() 동작 검증 테스트 추가

Context

사용자 제보: "입력하다가 IME 바꿔서 입력하면 마지막 단어가 날라감"

디버그 로그로 확인한 결과, 삼성 키보드가 전체 텍스트를 하나의 composition으로 유지하여(comp=TextRange(0, N)), IME 전환 시 commit 없이 composition이 통째로 버려지는 삼성 키보드 고유 동작이 원인. 이 부분은 앱 수준에서 완벽히 우회 불가.

본 PR은 앱 코드에서 제어 가능한 두 가지 문제를 수정:

  1. LaunchedEffect에서 TextFieldValue 재생성 시 composition 유실 방지
  2. Reply 진입 시 비동기 네트워크 응답이 사용자 입력을 덮어쓰는 경합 방지

Test plan

  • ./gradlew assembleDebug 빌드 성공
  • ComposeViewModelTest 추가 (setReplyTarget prefix 주입/비주입 검증)
  • Reply 진입 후 mention prefix 정상 주입 확인
  • Reply 진입 후 빠르게 입력 시 네트워크 응답이 입력을 덮어쓰지 않는지 확인

🤖 Generated with Claude Code

- Use TextFieldValue.copy() instead of constructor to preserve
  IME composition state during ViewModel-driven text sync
- Prevent setReplyTarget() from overwriting user-typed content
  when async network response arrives late
- Add ComposeViewModelTest for setReplyTarget() behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@malkoG malkoG merged commit a358674 into hackers-pub:main Apr 17, 2026
@dalinaum dalinaum deleted the fix/ime-composition-loss branch April 17, 2026 05:26
dalinaum added a commit to dalinaum/hackerspub-android that referenced this pull request Apr 17, 2026
Settings
- Add a leading back arrow in the Settings top bar so the screen can
  be exited without using the bottom-nav.
- Add a "Theme" row that opens a dialog with four options: Follow
  system / Light / Dark / Dynamic color (Material You). Dynamic is
  disabled with a "Requires Android 12+" caption on API < 31.

Theme system
- New ThemeMode enum persisted as a string preference in
  PreferencesManager so the choice survives restarts.
- HackersPubTheme now accepts a themeMode parameter and resolves the
  Material colorScheme accordingly. MainActivity collects the
  preference and passes it down before composing the app.
- For Dynamic mode, derive LocalAppColors from the wallpaper-based
  ColorScheme (surface tonal layers carry the hue; outline-based
  divider was too strong, mapped to surfaceContainerHigh to match the
  brand's near-invisible-divider convention).
- New AppColorScheme.composeOnAccent so the FAB content color tracks
  the theme; existing call sites (Timeline, PostDetail, Compose,
  ComposeArticle) drop the hard-coded Color.White.
- WallpaperManager.OnColorsChangedListener registered only while
  Dynamic is active so wallpaper hue changes trigger Activity
  recreation and pick up the fresh palette without an app restart.

Test fixes (pre-existing failures on main)
- ComposeViewModelTest: stub repository.getViewer() in @before — the
  init coroutine added in PR hackers-pub#120 crashed under MockK relaxed mode
  with a Result/Object cast error.
- ProfileViewModelTest: stub and verify getProfile(any(), any()) so
  the refresh-time call (refresh = true) is matched alongside the
  initial load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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