Name change: better unicode handling (#3164)

* Name change: better unicode handling

Client-side:

* Changes the NameChangeModal to show text "Over limit" when a proposed display
name is too long.

* Allows names to go over limit to prevent splitting graphemes on input.

Server-side:

* Changes the MakeSafeStringOfLength to count number of unicode code points
instead of string bytes.

* name modal: check that newName is defined before iterating
This commit is contained in:
John Regan 2023-07-11 13:44:09 -04:00 committed by GitHub
parent dfa3a2a273
commit 3f65099910
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 6 deletions

View file

@ -18,10 +18,14 @@ func MakeSafeStringOfLength(s string, length int) string {
newString := s
newString = StripHTML(newString)
if len(newString) > length {
newString = newString[:length]
// Convert utf-8 string into Unicode code points.
codePoints := []rune(newString)
if len(codePoints) > length {
codePoints = codePoints[:length]
}
newString = string(codePoints)
newString = strings.ReplaceAll(newString, "\r", "")
newString = strings.TrimSpace(newString)

View file

@ -31,14 +31,23 @@ export const NameChangeModal: FC<NameChangeModalProps> = ({ closeModal }) => {
const websocketService = useRecoilValue<WebsocketService>(websocketServiceAtom);
const [newName, setNewName] = useState<string>(currentUser?.displayName);
const characterLimit = 30;
if (!currentUser) {
return null;
}
const { displayName, displayColor } = currentUser;
const saveEnabled = () =>
newName !== displayName && newName !== '' && websocketService?.isConnected();
const saveEnabled = () => {
const count = newName !== undefined ? Array.from(newName).length : 0;
return (
newName !== displayName &&
count > 0 &&
count <= characterLimit &&
websocketService?.isConnected()
);
};
const handleNameChange = () => {
if (!saveEnabled()) return;
@ -59,6 +68,8 @@ export const NameChangeModal: FC<NameChangeModalProps> = ({ closeModal }) => {
websocketService.send(colorChange);
};
const showCount = info => (info.count > characterLimit ? 'Over limit' : '');
const maxColor = 8; // 0...n
const colorOptions = [...Array(maxColor)].map((_, i) => i);
@ -84,8 +95,7 @@ export const NameChangeModal: FC<NameChangeModalProps> = ({ closeModal }) => {
onChange={e => setNewName(e.target.value)}
placeholder="Your chat display name"
aria-label="Your chat display name"
maxLength={30}
showCount
showCount={{ formatter: showCount }}
defaultValue={displayName}
className={styles.inputGroup}
/>