Пользователь первый раз запустил приложение, ввёл email, получил письмо с подтверждением — и закрыл приложение, не кликнув по ссылке. Через минуту он открывает его снова. Логично ожидать экран «письмо отправлено, проверьте почту». Вместо этого приложение снова просит email. Если ввести его повторно — сервер создаст новый токен, и ссылка в старом письме перестанет работать. Когда человек наконец кликает по ней, получает «invalid link». Это классическая ловушка бинарного состояния.
Как выглядит ловушка
Цикл повторялся без шансов на выход:
- Ввод email → отправляется письмо с подтверждением.
- Пользователь закрывает приложение до клика.
- При следующем запуске снова показывается экран первого запуска.
- Повторный ввод email → сервер генерирует новый токен.
- Клик по ссылке из старого письма → «invalid link».
Корень проблемы — в том, как приложение хранило состояние регистрации. Оно было бинарным: «не зарегистрирован» или «зарегистрирован». Промежуточного состояния «письмо отправлено, но не подтверждено» просто не существовало. Поэтому любой перезапуск возвращал пользователя к началу.
Если состояние — не ноль и не единица, а несколько шагов с возможностью прерваться, бинарный флаг неизбежно отбрасывает человека назад.
Решение: сохранить pending_email
Фикс простой — в момент успешной отправки письма сохранить адрес в локальный конфиг app_config.json как pending_email. Тогда процесс регистрации получает третье состояние: «отправили, ждём подтверждения».
Важная деталь: сохранять адрес нужно только когда сервер ответил status: ok. Если отправить письмо не удалось, писать pending_email нельзя — пользователь будет вечно ждать письмо, которое не существует.
Сохранение оборачивается в try/except и не влияет на основной поток. Если записать в конфиг не получилось — приложение всё равно выполнило главную цель: отправило письмо. pending_email — это лишь страховка для возобновления.
Возврат к нужному экрану при старте
Теперь при запуске приложение проверяет статус. Если это первый запуск и в конфиге лежит pending_email, пользователь сразу попадает на экран «письмо отправлено, нажмите кнопку после перехода по ссылке». Вводить email заново не нужно.
И главное: экран ожидания не делает никаких обращений к серверу. Он просто берёт адрес из конфига и показывает его пользователю. Никакого нового токена.
Если бы при возобновлении приложение вызвало тот же endpoint регистрации, сервер опять выдал бы свежий токен — и первая ссылка снова умерла бы. Поэтому возобновление — это чисто зеркалирование локального состояния на экран.
Ручная отправка — отдельная история
Для случая, когда пользователь сам хочет переотправить письмо, на экране есть кнопка «Resend email». Это явное действие, поэтому генерация нового токена там допустима.
Ключевое различие: автоматическое возобновление не трогает токен, а ручная отправка обновляет его. Если перепутать эти два пути, вы вернёте ту же ловушку.
Когда очищать pending_email
Как только пользователь прошёл по ссылке и подтвердил email, pending_email нужно удалить. В тот же момент в конфиг записывается флаг email_registered и сам адрес как registered_email.
Так замыкается цикл состояний: не зарегистрирован → отправлено письмо (появляется pending_email) → подтверждено (удаляется pending_email, появляется registered_email). Где бы пользователь ни закрыл приложение, следующий запуск вернёт его на правильный шаг.
Сравнение подходов
| Сценарий | Бинарный флаг | pending_email |
|---|---|---|
| Закрыл приложение после отправки письма | При следующем запуске — экран ввода email | Экран «проверьте почту» |
| Повторный ввод email | Обязательный — иначе никак | Не нужен |
| Ссылка из старого письма | Перестаёт работать из-за нового токена | Остаётся валидной |
| Пользователь кликает по ссылке | «Invalid link» | Регистрация завершается |
Вывод
Изменение небольшое — добавить промежуточное состояние и сохранить его в конфиг. Но оно убирает целый класс разочарований: человеку больше не приходится повторять уже сделанные действия.
Этот паттерн применим к любому прерываемому процессу: загрузка файла, многошаговая форма, оплата. Если процесс может быть прерван, состояние нужно хранить не как «да/нет», а как фазу, которую можно продолжить с того же места.
Комментарии (0)
Войдите, чтобы комментировать.
Пока нет комментариев. Будьте первым.