fix: stop nickname truncation cutting a character in half

truncateNickname compared and cut with String.length and take, both of which
count UTF-16 code units. A name whose 15th unit is the high surrogate of an
emoji lost its low surrogate, leaving a lone U+D83D that renders as a tofu box
wherever the name is shown - the peer list, the geohash people list, and every
inline mention.

Step back to the last grapheme boundary at or before the limit instead, so a
truncated name is always whole. The limit itself is unchanged, so an ASCII
name truncates exactly as before.
This commit is contained in:
Taksh 2026-08-23 20:01:51 +05:30
parent d55754d824
commit 7073f7dec0

View File

@ -1,9 +1,19 @@
package com.bitchat.android.ui
import java.text.BreakIterator
/**
* UI constants/utilities for nickname rendering.
*/
fun truncateNickname(name: String, maxLen: Int = com.bitchat.android.util.AppConstants.UI.MAX_NICKNAME_LENGTH): String {
return if (name.length <= maxLen) name else name.take(maxLen)
}
if (name.length <= maxLen) return name
// The limit counts UTF-16 code units, so cutting at it can land inside a
// surrogate pair and leave a lone surrogate that renders as a tofu box, or
// split a ZWJ sequence into its parts. Step back to the last grapheme
// boundary at or before the limit so what is shown is always whole.
val boundaries = BreakIterator.getCharacterInstance()
boundaries.setText(name)
val end = boundaries.preceding(maxLen + 1)
return if (end == BreakIterator.DONE) "" else name.substring(0, end)
}