Web3-приложение: перезагрузка, офлайн и несколько вкладок
Оглавление курса «Web3-интерфейс: реестр в браузере» · Урок 29 из 30.
Посетитель может перезагрузить страницу, потерять сеть или открыть вторую вкладку. Это обычные события веб-приложения, а не исключительные поломки блокчейна. В этом уроке определим, какие данные восстанавливаются как черновик, какие — как наблюдение, а какие никогда не возобновляются автоматически. Результат — работа продолжается без неожиданной новой подписи.
Предполагаются журнал операций и схема кеша. Все программы остаются неисполненными; сценарии разбираются по модели состояния. Базовый реестр не получает новых методов. Наша задача — согласовать несколько представлений одного локального пользователя, сохраняя явный контроль над изменением сети.
Три вида сохранённых данных
Черновик формы содержит номер курса, отпечаток и пользовательское намерение. Он не доказывает права издателя и не является пригодной симуляцией. После восстановления нужно прочитать актуальный экземпляр, проверить аккаунт и сеть и подготовить запрос снова. Поле «готово к отправке» не следует сохранять как вечное разрешение.
Кеш карточки содержит прошлый снимок с номером и хешем блока. Его можно показать как последнее известное наблюдение, пока выполняется новое чтение. При отсутствии сети подпись прямо сообщает, что обновить сведения пока нельзя. Кеш не становится основанием автоматической записи и не выдаётся за свежее состояние.
Журнал отправленной операции содержит хеш и контекст. Он позволяет продолжить наблюдение receipt без кошелька. Если нет хеша из-за потерянного ответа отправки, журнал сохраняет неопределённость, а не конструирует фиктивную транзакцию. Разные виды данных имеют разные сроки жизни и уровни доверия.
Офлайн относится к транспорту
Браузерный признак наличия сети не гарантирует доступность конкретного RPC. Устройство может быть подключено к маршрутизатору без выхода к endpoint, а локальный узел может быть выключен при работающем интернете. Поэтому окончательное состояние чтения определяется попыткой обращения к нужному источнику, а не только событием online.
Во время офлайн интерфейс может оставить манифест или последнюю карточку доступными. Но он не обещает новую публикацию и не запрашивает подпись в расчёте «отправим позже незаметно». Очередь подписанных транзакций — отдельная сложная модель с nonce, сроками и сменой состояния. Наш учебный интерфейс не реализует такую очередь.
Когда связь возвращается, сначала обновляются чтение и незавершённые наблюдения. Сохранённый черновик не отправляется автоматически. Пользователь видит, что контекст мог измениться, и принимает новое решение. Это одинаково важно после короткого сбоя и после закрытия ноутбука на несколько дней.
Координация вкладок
Две вкладки могут показывать одну операцию и обе ждать receipt. Само повторное чтение не создаёт второй вызов контракта, но создаёт лишние запросы и конкурирующие записи журнала. Для небольшого приложения можно уведомлять другие вкладки о новом сохранённом факте через BroadcastChannel.
const channel = new BroadcastChannel('library-operations-v1');
export function announceObservedOperation(operation) {
channel.postMessage({
type: 'operation-updated',
id: operation.id,
chainId: operation.chainId,
currentHash: operation.currentHash,
});
}
channel.addEventListener('message', event => {
if (event.data?.type !== 'operation-updated') return;
// Прочитайте проверенную запись локального журнала и обновите наблюдение.
});
Это фрагмент уведомления, не блокировка подписи и не полноценный протокол лидерства. Сообщение не принимается как доказательство receipt; вкладка читает сохранённую запись и при необходимости сверяет её с RPC. Схема события проверяется, потому что другой код того же origin может отправить похожие данные.
BroadcastChannel не объединяет разные origin и не синхронизирует устройства через сервер. Для местного примера это полезное ограничение: данные не отправляются внешнему сервису автоматически. При закрытии приложения канал очищается соответствующим владельцем жизненного цикла. Отсутствие поддержки не должно ломать обычное чтение и журнал.
Двойная отправка из двух вкладок
Отключение кнопки в одной вкладке не отключает её во второй. Если пользователь отправит два publish с разными nonce, обе операции могут исполниться. Локальное уведомление уменьшает случайные повторы, но не является криптографической защитой. Контракт проверяет отправителя и аргументы, а не число открытых вкладок браузера.
Для публикации одинакового отпечатка интерфейс показывает предупреждение о незавершённой операции. Это всё ещё прикладная политика. В сложном процессе можно добавить контрактный идентификатор действия или условное ожидаемое состояние, но наш базовый реестр таких полей не имеет. Необходимо честно показывать границу простой модели.
Если две вкладки сохраняют разные статусы одного хеша, старое неизвестное наблюдение не должно перезаписать найденный receipt. Полезно хранить факты отдельно и увеличивать версию записи. Нельзя сравнивать качество наблюдений только по часам браузера: вкладки могут иметь задержки и случайный порядок завершения запросов.
Возврат в изменённую лабораторию
После сброса локального узла старый environmentId больше не соответствует текущему экземпляру. История сохраняется как архив, а карточка читает новую конфигурацию. Если использовать только chainId31337, приложение могло бы спутать прошлый хеш с текущим запуском. Поэтому поколение окружения входит в ключи кеша и журнал.
Сохранённый текст отпечатка можно предложить как черновик нового выпуска, но пользователь должен выбрать новый экземпляр и проверить происхождение файла. Старая симуляция не переносится между цепочками. Даже одинаковый адрес не является достаточной связью состояния после новой лаборатории.
История не заменяет резервную копию
LocalStorage и IndexedDB могут быть очищены пользователем или браузером. В них не размещаются единственные экземпляры важного редакционного журнала. Репозиторий хранит исходники и выпуск манифеста, процесс развёртывания — адрес и параметры экземпляра, а сетевые факты восстанавливаются по известному источнику. Браузерная история помогает продолжить работу, но не является полной стратегией восстановления.
Для упражнения рассмотрите две вкладки: первая получила receipt, вторая закончила старый тайм-аут позже. Определите, почему второе сообщение не должно понизить известный результат до неизвестного. Затем перезагрузите модель с новым environmentId: прежняя операция остаётся архивной. Ожидаемый итог — восстановление сохраняет контроль человека и происхождение данных. API координации описан в BroadcastChannel, хранение — в Web Storage.
Версия журнала между вкладками
Событие канала сообщает, что запись могла измениться, а не передаёт окончательную истину. Вкладка перечитывает нормализованный документ и сравнивает поколение наблюдения. Это позволяет обработать повторное сообщение безопасно и не возвращать статус назад при позднем событии. Известный receipt имеет собственное происхождение в блоке, которое сохраняется независимо от порядка браузерных сообщений.