chatmu · ticket 01 · re-pitch in Simplified Technical English

One press must do one thing — and it must do it once

A short version of where this task is. Sentences are short on purpose. Domain words are the words from CONTEXT.md.

0 · The words I use

Room
A place where Messages collect in time order. On the screen we call it 채널 when it has a name and is not a Direct Message.
Actor
A thing that can speak. It is not always a person.
User
A person account. A User logs in. The signup screen makes a User.
Message
What an Actor speaks in a Room. The author can change it or remove it later (ADR 0008).

1 · The problem

A person writes Korean text. An IME is active. The person presses a button. Nothing happens.

This is the chain of events:

  1. The IME is in composition. The person presses the button.
  2. The IME takes the first mousedown. The button does not get it.
  3. The browser makes a click only after a mousedown. There is no click.
  4. A form makes a submit only after a click. There is no submit.
  5. The handler does not run. The person sees no result.

The repair is known. Put the handler on pointerup and mouseup also.

The repair makes a second problem. A usual mouse press sends pointerup, then mouseup, then click. So one press calls the handler two or three times.

You must then count the calls and keep only one. This is the half that was missing.

2 · What I found

The repo wrote the first half by hand in many places. It wrote the second half in one place only.

The specification said six places. I counted seven.

One press does thisIt happens more than once if…What stopped it
1 Applies bold, italic or a listcomposer/Toolbar.tsx Bold turns on, then off, then on the button
2 Makes a Message in a Roomcomposer/PlateComposer.tsx The Room gets the same Message twice the handler
3 Opens the emoji panelcomposer/EmojiButton.tsx Nothing. To open twice is to open once the action itself
4 Makes a Userapp/signup/page.tsx The server gets three signup requests the handler
5 Changes the password of a Userapp/account/page.tsx The server gets the change twice the handler
6 Makes a Roomapp/rooms/RoomSidebar.tsx Two Rooms get the same name the handler
7 Changes a Messageapp/rooms/[roomId]/MessageEditSession.tsx The server gets the change twice the handler
Read the last column. Only place 1 keeps its safety in the button. Places 2, 4, 5, 6 and 7 keep their safety in the handler. A person who reads the button cannot see that safety.

Place 7 is the find

The specification did not list it. It was worse than the six.

It had mouseup only. It did not have pointerup.

No person saw a fault, because a flag in the handler stopped the second call.

That is the exact shape this ticket removes.

One place does not use the repair, and that is correct. The Leave Room button uses click only. A lost press only loses the confirm step. The person can press again. Do not put an action that you cannot undo on three events.

3 · What the tests saw

The browser tests count results. Three places already had a count.

PlaceTestWhat it measures
MessageCount of Messages in the Room = 1the handler
UserCount of signup requests = 1the handler
RoomCount of Rooms with that name = 1the handler
Format (bold)Bold is on after the pressthe button, but one path only
EmojiThe panel is openno count
Passwordno test
Message changeno count
The three green rows do not prove that the button is correct. Their handlers stop the second call. So the button can run three times, and the result is still one. No test could see the missing half. This is not an oversight. It is a limit of a test that looks from outside.

And that is the argument for the change: put the rule in the button, where a person can read it.

I added one test

A usual mouse press on the bold button must turn bold on one time.

Bold is a toggle. So the count shows in the result. No other place shows it.

The old test only used mouseup. No test used all three events.

This test cannot see an odd count. Three calls give the same result as one call. It sees two calls. Two calls is what you get when one of the two rules is deleted.

4 · What I did

Made one button: frontend/lib/pressOnce.tsx

The caller gives one handler. The button owns the four paths.

Why lib/: four of the seven places are screens. If the button lived in the composer module, the signup screen must import that module. That is the wrong direction. If it lived in the screens, the composer module must import a screen. That is the same wall from the other side.

Made "once" the default. Added no escape switch

Six of seven places need "once". One place does not care. Before, the safe behaviour was the exception.

An escape switch would put the decision back in the handler. That is the shape we remove.

Changed no handler

The flags and the "clear the input at once" rule stay. They stop a different thing: a person who really presses twice on a slow network. Two presses are two presses. A button cannot stop that.

I changed the comments only.

Kept type="submit" on the four form buttons

A form with two or more fields does not submit on Enter if it has no submit button. Signup has four fields.

So the button keeps the type, and it stops the default action of the click. The form then runs the work one time, not two.

5 · Status

I ran no checks. You must run them.

  • The worktree has no frontend/node_modules. So tsc, eslint and npm test cannot run.
  • This code did not get a type check.
  • The new browser test did not run one time.
  • I did not break the code on purpose to see a red test. The red-test list in the ticket is a prediction, not a measurement.

Left for later

Commits