sasha_par писал:
если сервер не ответил, честно пишу «не отправилось, попробуйте ещё раз»
Вот тут я бы поправил формулировку. «Сервер не ответил» и «не отправилось» — не одно и то же, а отсюда у меня этих дублей больше всего.
У вас сейчас вечер, у меня одиннадцать, почти ночь, и я как раз из тех посетителей, на которых такие формы ломаются. До европейских серверов отсюда обычно двести с чем-то миллисекунд, по мобильному в автобусе бывает заметно хуже, с провалами. Типичная картина: жму «отправить», кнопка честно пишет «отправляем…», запрос уходит, сервер его принимает и пишет строку в таблицу. А ответ обратно теряется где-то между тоннелем и сменой вышки. Фронт ждёт, срабатывает таймаут, и мне показывают «не отправилось, попробуйте ещё раз». Я пробую. В таблице две строки, и вторую я сделал не нервной рукой, а потому что форма сама меня попросила.
Поэтому я бы разделил два сообщения:
— сервер ответил ошибкой — тогда да, «не отправилось»;
— ответа не дождались — «не удалось подтвердить отправку», и повторная кнопка отправляет тот же токен, который выдали при открытии формы, а не новый.
Тогда повтор после таймаута безопасен. Если первая попытка дошла, сервер видит знакомый токен и просто возвращает «уже есть, спасибо». Если не дошла, записывает как новую. Человек ничего про это не знает, ему показывают одно и то же «спасибо, перезвоним».
Главное — не генерировать новый токен на каждый клик «попробовать ещё раз». Я видел, как токен перевыпускали при каждом сабмите, «чтобы был свежий», и вся защита от двойного клика переставала работать ровно там, где она нужна, на плохой связи. Токен живёт, пока живёт эта форма на экране, и меняется после успешной отправки, когда форма уже исчезла.
И пометка «возможный повтор» от ruslan_krd тут хорошо дополняет: токен ловит технические повторы, а через минуту с новой вкладки — уже человеческий случай, его пусть смотрит менеджер.
Проверить у себя легко, не надо ехать во Владивосток) В devtools браузера включить медленную сеть, нажать отправить и на середине запроса отключить сеть совсем. Если после этого в таблице появилась строка, а на экране «не отправилось» — ровно мой случай из автобуса.