ESP32: добавить NAV v8 с account и Wi-Fi reconnect

This commit is contained in:
AidarKC 2026-06-09 16:14:24 +04:00
parent e385bb6bf9
commit f4e7210a40
4 changed files with 386 additions and 35 deletions

View File

@ -3,7 +3,7 @@
- Краткое описание: минимальный UI-прототип для сабсервера на базе `LVGL + subserver touch`, с Wi-Fi flow, серверными адресами и общим экраном редактирования текста. - Краткое описание: минимальный UI-прототип для сабсервера на базе `LVGL + subserver touch`, с Wi-Fi flow, серверными адресами и общим экраном редактирования текста.
- Что проверять: - Что проверять:
- стартует экран `HOME`; - стартует экран `HOME`;
- на `HOME` видны `login`, `subserver1`, `W BAT`, `STATUS`, Wi-Fi статус и кнопка `SETTINGS`; - на `HOME` видны реальное значение логина или `login not set`, реальное значение сабсервера или `subserver not set`, при отсутствии секрета строка `secret not set`, а также `W BAT`, `STATUS`, Wi-Fi статус и кнопка `SETTINGS`;
- Wi-Fi статус на `HOME` корректно показывает одно из состояний: - Wi-Fi статус на `HOME` корректно показывает одно из состояний:
- `Wi-Fi not configured` - `Wi-Fi not configured`
- `Wi-Fi disconnected` - `Wi-Fi disconnected`
@ -21,16 +21,25 @@
- выбор SSID открывает общий экран редактирования текста для пароля; - выбор SSID открывает общий экран редактирования текста для пароля;
- на этом экране видно старое значение, курсор стоит в конце; - на этом экране видно старое значение, курсор стоит в конце;
- две верхние служебные строки над полем ввода отсутствуют; - две верхние служебные строки над полем ввода отсутствуют;
- при вводе пароля Wi-Fi текст показывается открыто, без точек;
- большая клавиатура реально видна на экране и занимает большую часть высоты; - большая клавиатура реально видна на экране и занимает большую часть высоты;
- буквы разбиты на 2 страницы; - буквы разбиты на 2 страницы;
- режим символов тоже разбит на 2 страницы; - режим символов тоже разбит на 2 страницы;
- на правой странице кнопки стоят в ровных вертикальных колонках; - на правой странице кнопки стоят в ровных вертикальных колонках;
- свайп влево/вправо на экране ввода переключает страницы клавиатуры; - свайп влево/вправо на экране ввода переключает страницы клавиатуры;
- при этом свайп страниц клавиатуры срабатывает только из нижней клавиатурной зоны, а не из верхней части экрана; - при этом свайп страниц клавиатуры срабатывает только из нижней клавиатурной зоны, а не из верхней части экрана;
- при переключении `ABC/123` и `SHIFT` уже введённый текст не пропадает;
- при свайпе между левой и правой половиной клавиатуры уже введённый текст тоже не пропадает, в том числе для цифр, символов и заглавных букв;
- визуальный курсор в поле ввода не показывается;
- новые символы всегда дописываются только в конец строки;
- основные 3 ряда клавиш и нижний служебный ряд стали выше;
- внизу остаётся отдельная тёмная полоса с версией `NAV v7`, а рамка клавиатурного блока заканчивается выше неё;
- `ABC/123`, `SHIFT`, `DEL`, `SAVE`, `CANCEL` работают; - `ABC/123`, `SHIFT`, `DEL`, `SAVE`, `CANCEL` работают;
- при успехе SSID и пароль сохраняются, а `HOME` показывает `Wi-Fi connected`; - при успехе SSID и пароль сохраняются, а `HOME` показывает `Wi-Fi connected`;
- при ошибке показывается `Connection failed`; - при ошибке показывается `Connection failed`;
- `CLEAR SAVED WI-FI` очищает сохранённые настройки; - `CLEAR SAVED WI-FI` очищает сохранённые настройки;
- если сеть была ранее успешно сохранена, после потери связи устройство автоматически пытается переподключиться;
- первые повторные попытки идут раз в `10` секунд, а после долгого отсутствия связи интервал увеличивается до `30` секунд;
- нажатие `Server` открывает `SERVER_SCREEN`; - нажатие `Server` открывает `SERVER_SCREEN`;
- в `SERVER_SCREEN` видны и редактируются два значения: - в `SERVER_SCREEN` видны и редактируются два значения:
- `https://api.devnet.solana.com` - `https://api.devnet.solana.com`
@ -38,7 +47,18 @@
- нажатие `SOLANA RPC` открывает общий экран редактирования; - нажатие `SOLANA RPC` открывает общий экран редактирования;
- нажатие `SHINE SERVER` открывает общий экран редактирования; - нажатие `SHINE SERVER` открывает общий экран редактирования;
- после `SAVE` новые адреса сохраняются в NVS; - после `SAVE` новые адреса сохраняются в NVS;
- нажатие `Account` открывает `ACCOUNT_SCREEN`;
- `ACCOUNT_SCREEN` показывает 3 кнопки:
- `Login (<value|not set>)`
- `Subserver (<value|not set>)`
- `Secret (<*****|not set>)`
- `Login` открывает общий экран редактирования и сохраняется в NVS;
- `Subserver` открывает промежуточный экран с `USE SUBSERVER1` и `EDIT MANUALLY`;
- `USE SUBSERVER1` возвращает стандартное значение `subserver1`;
- `EDIT MANUALLY` открывает общий экран редактирования и сохраняет значение в NVS;
- `Secret` открывает экран-заглушку, где сказано, что настройка ещё не реализована;
- свайп вправо из внутренних экранов возвращает в `SETTINGS_MENU`; - свайп вправо из внутренних экранов возвращает в `SETTINGS_MENU`;
- свайп вправо из `ACCOUNT_SUBSERVER_SCREEN` и `ACCOUNT_SECRET_SCREEN` возвращает в `ACCOUNT_SCREEN`;
- если во время реального свайпа палец проходит по кнопке, это не должно открывать кнопку как обычный `click`. - если во время реального свайпа палец проходит по кнопке, это не должно открывать кнопку как обычный `click`.
- Ожидаемый результат: новый скетч даёт чистый навигационный каркас и уже умеет настраивать Wi-Fi и серверные адреса на самой ESP32. - Ожидаемый результат: новый скетч даёт чистый навигационный каркас и уже умеет настраивать Wi-Fi и серверные адреса на самой ESP32.
- Статус: pending - Статус: pending

View File

@ -19,19 +19,22 @@
## Экраны ## Экраны
Прототип содержит 6 экранов: Прототип содержит 8 экранов:
- `HOME` - `HOME`
- `SETTINGS_MENU` - `SETTINGS_MENU`
- `WIFI_SCREEN` - `WIFI_SCREEN`
- `SERVER_SCREEN` - `SERVER_SCREEN`
- `ACCOUNT_SCREEN` - `ACCOUNT_SCREEN`
- `ACCOUNT_SUBSERVER_SCREEN`
- `ACCOUNT_SECRET_SCREEN`
- `TEXT_EDIT_SCREEN` - `TEXT_EDIT_SCREEN`
## HOME ## HOME
Показывает: Показывает:
- сверху слева `login`; - сверху слева значение логина или `login not set`;
- ниже `subserver1`; - ниже значение сабсервера или `subserver not set`;
- третьей строкой `secret not set`, если секрет ещё не помечен как установленный;
- сверху справа простые индикаторы `W BAT`; - сверху справа простые индикаторы `W BAT`;
- по центру крупный текст `STATUS`; - по центру крупный текст `STATUS`;
- статус Wi-Fi; - статус Wi-Fi;
@ -128,12 +131,44 @@
## ACCOUNT_SCREEN ## ACCOUNT_SCREEN
Показывает: Показывает:
- `Account` - заголовок `ACCOUNT`;
- `Account screen` - статусное сообщение;
- кнопку `Login (<value|not set>)`;
- кнопку `Subserver (<value|not set>)`;
- кнопку `Secret (<*****|not set>)`.
Переходы: Переходы:
- свайп вправо -> `SETTINGS_MENU` - свайп вправо -> `SETTINGS_MENU`
- кнопка `BACK` -> `SETTINGS_MENU` - `Login` -> `TEXT_EDIT_SCREEN`
- `Subserver` -> `ACCOUNT_SUBSERVER_SCREEN`
- `Secret` -> `ACCOUNT_SECRET_SCREEN`
## ACCOUNT_SUBSERVER_SCREEN
Показывает:
- текущий `subserver`;
- рекомендацию оставить `subserver1`, если устройство одно;
- кнопку `USE SUBSERVER1`;
- кнопку `EDIT MANUALLY`;
- кнопку `BACK`.
Переходы:
- `USE SUBSERVER1` -> сохраняет `subserver1` и возвращает в `ACCOUNT_SCREEN`
- `EDIT MANUALLY` -> `TEXT_EDIT_SCREEN`
- свайп вправо -> `ACCOUNT_SCREEN`
## ACCOUNT_SECRET_SCREEN
Пока это заглушка.
Показывает:
- текущий статус секрета `set/not set`;
- сообщение, что настройка секрета пока не реализована;
- кнопку `BACK`.
Переходы:
- свайп вправо -> `ACCOUNT_SCREEN`
- `BACK` -> `ACCOUNT_SCREEN`
## TEXT_EDIT_SCREEN ## TEXT_EDIT_SCREEN
@ -147,7 +182,9 @@
Показывает: Показывает:
- заголовок; - заголовок;
- поле ввода, уже заполненное старым значением; - поле ввода, уже заполненное старым значением;
- курсор установлен в конец текста; - новые символы всегда добавляются только в конец текста;
- визуальный курсор не показывается;
- пароль Wi-Fi показывается открыто, без маскировки точками;
- кнопки `SAVE`, `CANCEL`, `DEL`, `CLR`; - кнопки `SAVE`, `CANCEL`, `DEL`, `CLR`;
- большую экранную клавиатуру. - большую экранную клавиатуру.
@ -161,6 +198,8 @@
- переключение страниц выполняется свайпом влево/вправо внутри `TEXT_EDIT_SCREEN`; - переключение страниц выполняется свайпом влево/вправо внутри `TEXT_EDIT_SCREEN`;
- страница 1 содержит в основном буквы и базовые URL-символы; - страница 1 содержит в основном буквы и базовые URL-символы;
- страница 2 содержит цифры и дополнительные URL/символьные кнопки; - страница 2 содержит цифры и дополнительные URL/символьные кнопки;
- переключение `ABC/123` и `SHIFT` не должно очищать уже введённый текст;
- переключение страниц клавиатуры свайпом тоже не должно очищать уже введённый текст, включая цифры, символы и уже набранные заглавные буквы;
- есть специальные действия `DEL` и `CLR`. - есть специальные действия `DEL` и `CLR`.
## Хранение Wi-Fi ## Хранение Wi-Fi
@ -168,29 +207,45 @@
Используется `Preferences` (NVS памяти ESP32): Используется `Preferences` (NVS памяти ESP32):
- `wifi_ssid` - `wifi_ssid`
- `wifi_pass` - `wifi_pass`
- `wifi_known_good`
При старте устройства, если сохранён SSID, выполняется попытка подключения к сохранённой сети. При старте устройства, если сохранён SSID, выполняется попытка подключения к сохранённой сети.
Если сеть раньше уже была успешно подключена и помечена как валидная:
- после потери связи устройство автоматически пытается переподключиться;
- первые повторные попытки идут раз в `10` секунд;
- если связи нет долго, интервал увеличивается до `30` секунд.
## Хранение серверов ## Хранение серверов
Используется `Preferences` (NVS памяти ESP32): Используется `Preferences` (NVS памяти ESP32):
- `solana_rpc` - `solana_rpc`
- `shine_server` - `shine_server`
## Хранение аккаунта
Используется `Preferences` (NVS памяти ESP32):
- `login`
- `subserver`
- `secret_set`
## Детали клавиатуры ## Детали клавиатуры
- клавиатура занимает примерно `2/3`-`3/4` высоты экрана; - клавиатура занимает примерно `2/3`-`3/4` высоты экрана;
- сверху остаются только заголовок, подсказка и поле ввода; - сверху остаются только заголовок и поле ввода;
- буквы занимают 3 ряда; - буквы занимают 3 ряда;
- половина букв находится на левой странице, половина на правой; - половина букв находится на левой странице, половина на правой;
- на правой странице кнопки тоже стоят в ровных колонках, без сдвига рядов вправо; - на правой странице кнопки тоже стоят в ровных колонках, без сдвига рядов вправо;
- отдельный режим `symbols` тоже разделён на 2 страницы; - отдельный режим `symbols` тоже разделён на 2 страницы;
- 3 основных ряда клавиш и нижний служебный ряд увеличены по высоте;
- клавиши дополнительно увеличены по высоте по сравнению с предыдущим промежуточным вариантом;
- четвёртый ряд содержит: - четвёртый ряд содержит:
- переключение `ABC/123` - переключение `ABC/123`
- `SHIFT` - `SHIFT`
- `DEL` - `DEL`
- `SAVE` - `SAVE`
- `CANCEL` - `CANCEL`
- ниже рамки клавиатурного блока остаётся отдельная тёмная полоса с версией `NAV v7`.
## Жесты ## Жесты
@ -207,6 +262,8 @@
- `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU` - `WIFI_SCREEN`: свайп вправо -> `SETTINGS_MENU`
- `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU` - `SERVER_SCREEN`: свайп вправо -> `SETTINGS_MENU`
- `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU` - `ACCOUNT_SCREEN`: свайп вправо -> `SETTINGS_MENU`
- `ACCOUNT_SUBSERVER_SCREEN`: свайп вправо -> `ACCOUNT_SCREEN`
- `ACCOUNT_SECRET_SCREEN`: свайп вправо -> `ACCOUNT_SCREEN`
- `TEXT_EDIT_SCREEN`: свайп влево/вправо -> переключение страниц клавиатуры - `TEXT_EDIT_SCREEN`: свайп влево/вправо -> переключение страниц клавиатуры
- переключение страниц клавиатуры срабатывает только если свайп начался в зоне самой клавиатуры, а не по всему экрану редактора - переключение страниц клавиатуры срабатывает только если свайп начался в зоне самой клавиатуры, а не по всему экрану редактора

View File

@ -25,11 +25,14 @@
#define TAP_CANCEL_THRESHOLD 18 #define TAP_CANCEL_THRESHOLD 18
#define MAX_SCAN_RESULTS 8 #define MAX_SCAN_RESULTS 8
#define WIFI_CONNECT_TIMEOUT_MS 12000 #define WIFI_CONNECT_TIMEOUT_MS 12000
#define WIFI_RECONNECT_FAST_MS 10000
#define WIFI_RECONNECT_SLOW_MS 30000
#define WIFI_RECONNECT_SLOW_AFTER_MS 90000
#define TEXT_EDIT_PANEL_X 10 #define TEXT_EDIT_PANEL_X 10
#define TEXT_EDIT_PANEL_Y 126 #define TEXT_EDIT_PANEL_Y 112
#define TEXT_EDIT_PANEL_W 460 #define TEXT_EDIT_PANEL_W 460
#define TEXT_EDIT_PANEL_H 344 #define TEXT_EDIT_PANEL_H 330
#define TEST_VERSION "NAV v6" #define TEST_VERSION "NAV v8"
enum Screen { enum Screen {
SCREEN_HOME, SCREEN_HOME,
@ -37,6 +40,8 @@ enum Screen {
SCREEN_WIFI, SCREEN_WIFI,
SCREEN_SERVER, SCREEN_SERVER,
SCREEN_ACCOUNT, SCREEN_ACCOUNT,
SCREEN_ACCOUNT_SUBSERVER,
SCREEN_ACCOUNT_SECRET,
SCREEN_TEXT_EDIT, SCREEN_TEXT_EDIT,
}; };
@ -60,6 +65,12 @@ enum ActionId {
ACTION_WIFI_CLEAR, ACTION_WIFI_CLEAR,
ACTION_SERVER_EDIT_SOLANA, ACTION_SERVER_EDIT_SOLANA,
ACTION_SERVER_EDIT_SHINE, ACTION_SERVER_EDIT_SHINE,
ACTION_ACCOUNT_EDIT_LOGIN,
ACTION_ACCOUNT_EDIT_SUBSERVER,
ACTION_ACCOUNT_EDIT_SECRET,
ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT,
ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL,
ACTION_BACK_ACCOUNT,
ACTION_EDITOR_SAVE, ACTION_EDITOR_SAVE,
ACTION_EDITOR_CANCEL, ACTION_EDITOR_CANCEL,
}; };
@ -74,6 +85,8 @@ enum EditContext {
EDIT_CONTEXT_WIFI_PASSWORD, EDIT_CONTEXT_WIFI_PASSWORD,
EDIT_CONTEXT_SOLANA_RPC, EDIT_CONTEXT_SOLANA_RPC,
EDIT_CONTEXT_SHINE_SERVER, EDIT_CONTEXT_SHINE_SERVER,
EDIT_CONTEXT_LOGIN,
EDIT_CONTEXT_SUBSERVER,
}; };
enum KeyboardMode { enum KeyboardMode {
@ -118,6 +131,16 @@ static WifiViewMode gWifiViewMode = WIFI_VIEW_OVERVIEW;
static String gSolanaRpcUrl = "https://api.devnet.solana.com"; static String gSolanaRpcUrl = "https://api.devnet.solana.com";
static String gShineServerUrl = "https://shineup.me"; static String gShineServerUrl = "https://shineup.me";
static String gServerStatusMessage = "Edit RPC or shine host"; static String gServerStatusMessage = "Edit RPC or shine host";
static String gLoginValue;
static String gSubserverValue = "subserver1";
static bool gSecretConfigured = false;
static String gAccountStatusMessage = "Edit account fields";
static bool gWifiKnownGood = false;
static bool gWifiReconnectEnabled = false;
static bool gWifiOperationBusy = false;
static unsigned long gWifiDisconnectedSinceMs = 0;
static unsigned long gWifiLastReconnectAttemptMs = 0;
static wl_status_t gLastWifiStatus = WL_IDLE_STATUS;
static EditContext gEditContext = EDIT_CONTEXT_NONE; static EditContext gEditContext = EDIT_CONTEXT_NONE;
static Screen gEditReturnScreen = SCREEN_HOME; static Screen gEditReturnScreen = SCREEN_HOME;
@ -136,11 +159,14 @@ static void loadPrefs();
static void saveWifiPrefs(); static void saveWifiPrefs();
static void clearWifiPrefs(); static void clearWifiPrefs();
static void saveServerPrefs(); static void saveServerPrefs();
static void saveAccountPrefs();
static void beginSavedWifi(); static void beginSavedWifi();
static void scanWifiNetworks(); static void scanWifiNetworks();
static bool connectWifiNow(const String &ssid, const String &password); static bool connectWifiNow(const String &ssid, const String &password);
static String wifiHomeStatus(); static String wifiHomeStatus();
static String wifiSavedLabel(); static String wifiSavedLabel();
static void updateWifiReconnectState();
static void manageWifiReconnect();
static void openEditor(EditContext context, static void openEditor(EditContext context,
Screen returnScreen, Screen returnScreen,
const String &title, const String &title,
@ -149,6 +175,13 @@ static void openEditor(EditContext context,
bool isPassword); bool isPassword);
static void applyEditorValue(); static void applyEditorValue();
static bool isTextEditKeyboardSwipeArea(int16_t x, int16_t y); static bool isTextEditKeyboardSwipeArea(int16_t x, int16_t y);
static void syncEditValueFromTextarea();
static void keepCursorAtEnd();
static void restoreTextareaFromEditValue();
static String loginDisplayValue();
static String subserverDisplayValue();
static String homeSecretStatus();
static String secretButtonValue();
static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) { static void lvglFlushCb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colorP) {
uint32_t w = area->x2 - area->x1 + 1; uint32_t w = area->x2 - area->x1 + 1;
@ -263,13 +296,18 @@ static void showMessageAt(const String &text, lv_coord_t y) {
static void loadPrefs() { static void loadPrefs() {
gWifiSavedSsid = gPrefs.getString("wifi_ssid", ""); gWifiSavedSsid = gPrefs.getString("wifi_ssid", "");
gWifiSavedPassword = gPrefs.getString("wifi_pass", ""); gWifiSavedPassword = gPrefs.getString("wifi_pass", "");
gWifiKnownGood = gPrefs.getBool("wifi_known_good", false);
gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com"); gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com");
gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me"); gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me");
gLoginValue = gPrefs.getString("login", "");
gSubserverValue = gPrefs.getString("subserver", "subserver1");
gSecretConfigured = gPrefs.getBool("secret_set", false);
} }
static void saveWifiPrefs() { static void saveWifiPrefs() {
gPrefs.putString("wifi_ssid", gWifiSavedSsid); gPrefs.putString("wifi_ssid", gWifiSavedSsid);
gPrefs.putString("wifi_pass", gWifiSavedPassword); gPrefs.putString("wifi_pass", gWifiSavedPassword);
gPrefs.putBool("wifi_known_good", gWifiKnownGood);
} }
static void saveServerPrefs() { static void saveServerPrefs() {
@ -277,22 +315,36 @@ static void saveServerPrefs() {
gPrefs.putString("shine_server", gShineServerUrl); gPrefs.putString("shine_server", gShineServerUrl);
} }
static void saveAccountPrefs() {
gPrefs.putString("login", gLoginValue);
gPrefs.putString("subserver", gSubserverValue);
gPrefs.putBool("secret_set", gSecretConfigured);
}
static void clearWifiPrefs() { static void clearWifiPrefs() {
gWifiSavedSsid = ""; gWifiSavedSsid = "";
gWifiSavedPassword = ""; gWifiSavedPassword = "";
gWifiSelectedSsid = ""; gWifiSelectedSsid = "";
gWifiKnownGood = false;
gWifiReconnectEnabled = false;
gWifiDisconnectedSinceMs = 0;
gWifiLastReconnectAttemptMs = 0;
saveWifiPrefs(); saveWifiPrefs();
WiFi.disconnect(true, false); WiFi.disconnect(true, false);
gWifiStatusMessage = "Saved Wi-Fi cleared"; gWifiStatusMessage = "Saved Wi-Fi cleared";
} }
static void beginSavedWifi() { static void beginSavedWifi() {
if (gWifiSavedSsid.isEmpty()) { if (gWifiSavedSsid.isEmpty() || !gWifiKnownGood) {
gWifiReconnectEnabled = false;
return; return;
} }
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.begin(gWifiSavedSsid.c_str(), gWifiSavedPassword.c_str()); WiFi.begin(gWifiSavedSsid.c_str(), gWifiSavedPassword.c_str());
gWifiReconnectEnabled = true;
gWifiDisconnectedSinceMs = millis();
gWifiLastReconnectAttemptMs = millis();
} }
static String wifiHomeStatus() { static String wifiHomeStatus() {
@ -312,9 +364,78 @@ static String wifiSavedLabel() {
return String("Saved: ") + gWifiSavedSsid; return String("Saved: ") + gWifiSavedSsid;
} }
static String loginDisplayValue() {
return gLoginValue.isEmpty() ? "login not set" : gLoginValue;
}
static String subserverDisplayValue() {
return gSubserverValue.isEmpty() ? "subserver not set" : gSubserverValue;
}
static String homeSecretStatus() {
return gSecretConfigured ? "" : "secret not set";
}
static String secretButtonValue() {
return gSecretConfigured ? "*****" : "not set";
}
static void updateWifiReconnectState() {
wl_status_t status = WiFi.status();
if (status == WL_CONNECTED) {
if (gLastWifiStatus != WL_CONNECTED) {
gWifiStatusMessage = String("Connected: ") + WiFi.localIP().toString();
}
gWifiDisconnectedSinceMs = 0;
gLastWifiStatus = status;
return;
}
if (gLastWifiStatus == WL_CONNECTED && gWifiReconnectEnabled) {
gWifiDisconnectedSinceMs = millis();
gWifiLastReconnectAttemptMs = 0;
gWifiStatusMessage = "Wi-Fi lost. Reconnecting...";
}
gLastWifiStatus = status;
}
static void manageWifiReconnect() {
updateWifiReconnectState();
if (!gWifiReconnectEnabled || gWifiSavedSsid.isEmpty() || gWifiOperationBusy) {
return;
}
if (WiFi.status() == WL_CONNECTED) {
return;
}
if (gWifiDisconnectedSinceMs == 0) {
gWifiDisconnectedSinceMs = millis();
}
unsigned long disconnectedFor = millis() - gWifiDisconnectedSinceMs;
unsigned long interval = disconnectedFor >= WIFI_RECONNECT_SLOW_AFTER_MS
? WIFI_RECONNECT_SLOW_MS
: WIFI_RECONNECT_FAST_MS;
if (gWifiLastReconnectAttemptMs != 0 &&
millis() - gWifiLastReconnectAttemptMs < interval) {
return;
}
gWifiLastReconnectAttemptMs = millis();
gWifiStatusMessage = disconnectedFor >= WIFI_RECONNECT_SLOW_AFTER_MS
? "Wi-Fi retry every 30s"
: "Wi-Fi retry every 10s";
WiFi.disconnect(false, false);
WiFi.mode(WIFI_STA);
WiFi.begin(gWifiSavedSsid.c_str(), gWifiSavedPassword.c_str());
}
static void scanWifiNetworks() { static void scanWifiNetworks() {
gScanResultCount = 0; gScanResultCount = 0;
gWifiStatusMessage = "Scanning networks..."; gWifiStatusMessage = "Scanning networks...";
gWifiOperationBusy = true;
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.disconnect(false, false); WiFi.disconnect(false, false);
delay(100); delay(100);
@ -323,6 +444,7 @@ static void scanWifiNetworks() {
if (found <= 0) { if (found <= 0) {
gWifiStatusMessage = "No networks found"; gWifiStatusMessage = "No networks found";
gWifiViewMode = WIFI_VIEW_OVERVIEW; gWifiViewMode = WIFI_VIEW_OVERVIEW;
gWifiOperationBusy = false;
return; return;
} }
@ -333,6 +455,7 @@ static void scanWifiNetworks() {
WiFi.scanDelete(); WiFi.scanDelete();
gWifiStatusMessage = String("Found ") + found + " networks"; gWifiStatusMessage = String("Found ") + found + " networks";
gWifiViewMode = WIFI_VIEW_SCAN_RESULTS; gWifiViewMode = WIFI_VIEW_SCAN_RESULTS;
gWifiOperationBusy = false;
} }
static bool connectWifiNow(const String &ssid, const String &password) { static bool connectWifiNow(const String &ssid, const String &password) {
@ -340,6 +463,7 @@ static bool connectWifiNow(const String &ssid, const String &password) {
rebuildScreen(); rebuildScreen();
lv_timer_handler(); lv_timer_handler();
gWifiOperationBusy = true;
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.disconnect(false, false); WiFi.disconnect(false, false);
delay(150); delay(150);
@ -350,8 +474,14 @@ static bool connectWifiNow(const String &ssid, const String &password) {
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
gWifiSavedSsid = ssid; gWifiSavedSsid = ssid;
gWifiSavedPassword = password; gWifiSavedPassword = password;
gWifiKnownGood = true;
gWifiReconnectEnabled = true;
gWifiDisconnectedSinceMs = 0;
gWifiLastReconnectAttemptMs = 0;
gLastWifiStatus = WL_CONNECTED;
saveWifiPrefs(); saveWifiPrefs();
gWifiStatusMessage = String("Connected: ") + WiFi.localIP().toString(); gWifiStatusMessage = String("Connected: ") + WiFi.localIP().toString();
gWifiOperationBusy = false;
return true; return true;
} }
delay(200); delay(200);
@ -359,6 +489,7 @@ static bool connectWifiNow(const String &ssid, const String &password) {
WiFi.disconnect(false, false); WiFi.disconnect(false, false);
gWifiStatusMessage = "Connection failed"; gWifiStatusMessage = "Connection failed";
gWifiOperationBusy = false;
return false; return false;
} }
@ -409,6 +540,24 @@ static void applyEditorValue() {
showScreen(SCREEN_SERVER); showScreen(SCREEN_SERVER);
return; return;
} }
if (gEditContext == EDIT_CONTEXT_LOGIN) {
value.trim();
gLoginValue = value;
saveAccountPrefs();
gAccountStatusMessage = gLoginValue.isEmpty() ? "Login cleared" : "Login saved";
showScreen(SCREEN_ACCOUNT);
return;
}
if (gEditContext == EDIT_CONTEXT_SUBSERVER) {
value.trim();
gSubserverValue = value;
saveAccountPrefs();
gAccountStatusMessage = gSubserverValue.isEmpty() ? "Subserver cleared" : "Subserver saved";
showScreen(SCREEN_ACCOUNT);
return;
}
} }
static void cancelEditor() { static void cancelEditor() {
@ -419,6 +568,25 @@ static void cancelEditor() {
} }
} }
static void syncEditValueFromTextarea() {
if (gInputTextArea) {
gEditValue = String(lv_textarea_get_text(gInputTextArea));
}
}
static void keepCursorAtEnd() {
if (gInputTextArea) {
lv_textarea_set_cursor_pos(gInputTextArea, LV_TEXTAREA_CURSOR_LAST);
}
}
static void restoreTextareaFromEditValue() {
if (gInputTextArea) {
lv_textarea_set_text(gInputTextArea, gEditValue.c_str());
keepCursorAtEnd();
}
}
static void networkSelectCb(lv_event_t *event) { static void networkSelectCb(lv_event_t *event) {
if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) { if (lv_event_get_code(event) != LV_EVENT_CLICKED || gBlockClick) {
return; return;
@ -450,10 +618,14 @@ static void editorKeyCb(lv_event_t *event) {
if (strcmp(token, "<DEL>") == 0) { if (strcmp(token, "<DEL>") == 0) {
lv_textarea_del_char(gInputTextArea); lv_textarea_del_char(gInputTextArea);
syncEditValueFromTextarea();
keepCursorAtEnd();
return; return;
} }
if (strcmp(token, "<CLR>") == 0) { if (strcmp(token, "<CLR>") == 0) {
lv_textarea_set_text(gInputTextArea, ""); lv_textarea_set_text(gInputTextArea, "");
syncEditValueFromTextarea();
keepCursorAtEnd();
return; return;
} }
if (strcmp(token, "<SAVE>") == 0) { if (strcmp(token, "<SAVE>") == 0) {
@ -465,12 +637,14 @@ static void editorKeyCb(lv_event_t *event) {
return; return;
} }
if (strcmp(token, "<MODE>") == 0) { if (strcmp(token, "<MODE>") == 0) {
syncEditValueFromTextarea();
gKeyboardMode = (gKeyboardMode == KEYBOARD_MODE_ALPHA) ? KEYBOARD_MODE_SYMBOLS : KEYBOARD_MODE_ALPHA; gKeyboardMode = (gKeyboardMode == KEYBOARD_MODE_ALPHA) ? KEYBOARD_MODE_SYMBOLS : KEYBOARD_MODE_ALPHA;
gKeyboardPage = 0; gKeyboardPage = 0;
rebuildScreen(); rebuildScreen();
return; return;
} }
if (strcmp(token, "<SHIFT>") == 0) { if (strcmp(token, "<SHIFT>") == 0) {
syncEditValueFromTextarea();
gKeyboardShift = !gKeyboardShift; gKeyboardShift = !gKeyboardShift;
rebuildScreen(); rebuildScreen();
return; return;
@ -481,7 +655,10 @@ static void editorKeyCb(lv_event_t *event) {
out.toUpperCase(); out.toUpperCase();
} }
keepCursorAtEnd();
lv_textarea_add_text(gInputTextArea, out.c_str()); lv_textarea_add_text(gInputTextArea, out.c_str());
syncEditValueFromTextarea();
keepCursorAtEnd();
} }
static void actionButtonCb(lv_event_t *event) { static void actionButtonCb(lv_event_t *event) {
@ -504,6 +681,9 @@ static void actionButtonCb(lv_event_t *event) {
case ACTION_OPEN_ACCOUNT: case ACTION_OPEN_ACCOUNT:
showScreen(SCREEN_ACCOUNT); showScreen(SCREEN_ACCOUNT);
break; break;
case ACTION_BACK_ACCOUNT:
showScreen(SCREEN_ACCOUNT);
break;
case ACTION_BACK_HOME: case ACTION_BACK_HOME:
showScreen(SCREEN_HOME); showScreen(SCREEN_HOME);
break; break;
@ -524,7 +704,7 @@ static void actionButtonCb(lv_event_t *event) {
openEditor(EDIT_CONTEXT_SOLANA_RPC, openEditor(EDIT_CONTEXT_SOLANA_RPC,
SCREEN_SERVER, SCREEN_SERVER,
"EDIT SOLANA RPC", "EDIT SOLANA RPC",
"Cursor starts at the end.", "",
gSolanaRpcUrl, gSolanaRpcUrl,
false); false);
break; break;
@ -532,10 +712,38 @@ static void actionButtonCb(lv_event_t *event) {
openEditor(EDIT_CONTEXT_SHINE_SERVER, openEditor(EDIT_CONTEXT_SHINE_SERVER,
SCREEN_SERVER, SCREEN_SERVER,
"EDIT SHINE HOST", "EDIT SHINE HOST",
"Cursor starts at the end.", "",
gShineServerUrl, gShineServerUrl,
false); false);
break; break;
case ACTION_ACCOUNT_EDIT_LOGIN:
openEditor(EDIT_CONTEXT_LOGIN,
SCREEN_ACCOUNT,
"EDIT LOGIN",
"",
gLoginValue,
false);
break;
case ACTION_ACCOUNT_EDIT_SUBSERVER:
showScreen(SCREEN_ACCOUNT_SUBSERVER);
break;
case ACTION_ACCOUNT_EDIT_SECRET:
showScreen(SCREEN_ACCOUNT_SECRET);
break;
case ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT:
gSubserverValue = "subserver1";
saveAccountPrefs();
gAccountStatusMessage = "Subserver set to subserver1";
showScreen(SCREEN_ACCOUNT);
break;
case ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL:
openEditor(EDIT_CONTEXT_SUBSERVER,
SCREEN_ACCOUNT,
"EDIT SUBSERVER",
"",
gSubserverValue,
false);
break;
case ACTION_EDITOR_SAVE: case ACTION_EDITOR_SAVE:
applyEditorValue(); applyEditorValue();
break; break;
@ -569,6 +777,9 @@ static lv_obj_t *makeButton(const char *text,
lv_obj_t *label = lv_label_create(btn); lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, text); lv_label_set_text(label, text);
lv_obj_set_width(label, w - 24);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_obj_set_style_text_font(label, font, 0); lv_obj_set_style_text_font(label, font, 0);
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
lv_obj_center(label); lv_obj_center(label);
@ -622,17 +833,25 @@ static void drawHome() {
setRootStyle(); setRootStyle();
lv_obj_t *login = lv_label_create(gRoot); lv_obj_t *login = lv_label_create(gRoot);
lv_label_set_text(login, "login"); lv_label_set_text(login, loginDisplayValue().c_str());
lv_obj_set_style_text_font(login, &lv_font_montserrat_18, 0); lv_obj_set_style_text_font(login, &lv_font_montserrat_18, 0);
lv_obj_set_style_text_color(login, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_color(login, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(login, LV_ALIGN_TOP_LEFT, 24, 18); lv_obj_align(login, LV_ALIGN_TOP_LEFT, 24, 18);
lv_obj_t *subserver = lv_label_create(gRoot); lv_obj_t *subserver = lv_label_create(gRoot);
lv_label_set_text(subserver, "subserver1"); lv_label_set_text(subserver, subserverDisplayValue().c_str());
lv_obj_set_style_text_font(subserver, &lv_font_montserrat_18, 0); lv_obj_set_style_text_font(subserver, &lv_font_montserrat_18, 0);
lv_obj_set_style_text_color(subserver, lv_color_hex(0xD5DEE7), 0); lv_obj_set_style_text_color(subserver, lv_color_hex(0xD5DEE7), 0);
lv_obj_align_to(subserver, login, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6); lv_obj_align_to(subserver, login, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
if (!gSecretConfigured) {
lv_obj_t *secret = lv_label_create(gRoot);
lv_label_set_text(secret, homeSecretStatus().c_str());
lv_obj_set_style_text_font(secret, &lv_font_montserrat_16, 0);
lv_obj_set_style_text_color(secret, lv_color_hex(0xB8C6D3), 0);
lv_obj_align_to(secret, subserver, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 6);
}
makeTopIcons(); makeTopIcons();
makeTitle("STATUS", 150, &lv_font_montserrat_28); makeTitle("STATUS", 150, &lv_font_montserrat_28);
showMessageAt(wifiHomeStatus(), 204); showMessageAt(wifiHomeStatus(), 204);
@ -747,10 +966,38 @@ static void drawServerScreen() {
static void drawAccountScreen() { static void drawAccountScreen() {
setRootStyle(); setRootStyle();
makeTitle("ACCOUNT", 56, &lv_font_montserrat_28);
makeBody("Account screen", 132, 400); makeTitle("ACCOUNT", 18, &lv_font_montserrat_24);
makeBody("Swipe right to return to Settings.", 204, 400); showMessageAt(gAccountStatusMessage, 56);
makeButton("BACK", 140, 344, 200, 72, 0x5A6570, ACTION_BACK_SETTINGS, &lv_font_montserrat_22);
String loginButton = String("Login (") + (gLoginValue.isEmpty() ? "not set" : gLoginValue) + ")";
String subserverButton = String("Subserver (") + (gSubserverValue.isEmpty() ? "not set" : gSubserverValue) + ")";
String secretButton = String("Secret (") + secretButtonValue() + ")";
makeButton(loginButton.c_str(), 22, 118, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_LOGIN, &lv_font_montserrat_20);
makeButton(subserverButton.c_str(), 22, 222, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_SUBSERVER, &lv_font_montserrat_20);
makeButton(secretButton.c_str(), 22, 326, 436, 84, 0x355C7D, ACTION_ACCOUNT_EDIT_SECRET, &lv_font_montserrat_20);
makeBody("Swipe right to return to Settings.", 420, 420);
makeVersionTag();
}
static void drawAccountSubserverScreen() {
setRootStyle();
makeTitle("SUBSERVER", 18, &lv_font_montserrat_24);
showMessageAt(String("Current: ") + subserverDisplayValue(), 56);
makeBody("If you only use one subserver, keep the default name subserver1.", 98, 420);
makeButton("USE SUBSERVER1", 22, 202, 436, 84, 0x2A9D8F, ACTION_ACCOUNT_SUBSERVER_USE_DEFAULT, &lv_font_montserrat_22);
makeButton("EDIT MANUALLY", 22, 306, 436, 84, 0x355C7D, ACTION_ACCOUNT_SUBSERVER_EDIT_MANUAL, &lv_font_montserrat_22);
makeButton("BACK", 140, 402, 200, 54, 0x5A6570, ACTION_BACK_ACCOUNT, &lv_font_montserrat_20);
makeVersionTag();
}
static void drawAccountSecretScreen() {
setRootStyle();
makeTitle("SECRET", 18, &lv_font_montserrat_24);
showMessageAt(String("Status: ") + (gSecretConfigured ? "set" : "not set"), 56);
makeBody("Secret setup is not implemented yet.", 116, 420);
makeButton("BACK", 140, 360, 200, 72, 0x5A6570, ACTION_BACK_ACCOUNT, &lv_font_montserrat_22);
makeVersionTag(); makeVersionTag();
} }
@ -788,14 +1035,14 @@ static void drawTextEditorKeyboard(lv_coord_t originY) {
static const char *symRightRow1[] = {"?", "&", "=", "+", "-"}; static const char *symRightRow1[] = {"?", "&", "=", "+", "-"};
static const char *symRightRow2[] = {"%", "#", "~", ":", "/"}; static const char *symRightRow2[] = {"%", "#", "~", ":", "/"};
const lv_coord_t keyH = 50; const lv_coord_t keyH = 62;
const lv_coord_t keyW5 = 84; const lv_coord_t keyW5 = 84;
const lv_coord_t keyW4 = 104; const lv_coord_t keyW4 = 84;
const lv_coord_t gap = 8; const lv_coord_t gap = 8;
const lv_coord_t row0Y = originY; const lv_coord_t row0Y = originY;
const lv_coord_t row1Y = originY + 58; const lv_coord_t row1Y = originY + 68;
const lv_coord_t row2Y = originY + 116; const lv_coord_t row2Y = originY + 136;
const lv_coord_t row3Y = originY + 180; const lv_coord_t row3Y = originY + 208;
const bool uppercase = gKeyboardMode == KEYBOARD_MODE_ALPHA && gKeyboardShift; const bool uppercase = gKeyboardMode == KEYBOARD_MODE_ALPHA && gKeyboardShift;
if (gKeyboardMode == KEYBOARD_MODE_ALPHA) { if (gKeyboardMode == KEYBOARD_MODE_ALPHA) {
@ -820,11 +1067,11 @@ static void drawTextEditorKeyboard(lv_coord_t originY) {
} }
} }
makeKeyboardButton(gKeyboardMode == KEYBOARD_MODE_ALPHA ? "123" : "ABC", "<MODE>", 20, row3Y, 88, 54, 0x5C6F82, &lv_font_montserrat_18); makeKeyboardButton(gKeyboardMode == KEYBOARD_MODE_ALPHA ? "123" : "ABC", "<MODE>", 20, row3Y, 88, 64, 0x5C6F82, &lv_font_montserrat_18);
makeKeyboardButton(gKeyboardShift ? "SHIFT*" : "SHIFT", "<SHIFT>", 116, row3Y, 88, 54, 0x7A5C9B, &lv_font_montserrat_16); makeKeyboardButton(gKeyboardShift ? "SHIFT*" : "SHIFT", "<SHIFT>", 116, row3Y, 88, 64, 0x7A5C9B, &lv_font_montserrat_16);
makeKeyboardButton("DEL", "<DEL>", 212, row3Y, 76, 54, 0x7D3A3A, &lv_font_montserrat_18); makeKeyboardButton("DEL", "<DEL>", 212, row3Y, 76, 64, 0x7D3A3A, &lv_font_montserrat_18);
makeKeyboardButton("SAVE", "<SAVE>", 296, row3Y, 76, 54, 0x2A9D8F, &lv_font_montserrat_18); makeKeyboardButton("SAVE", "<SAVE>", 296, row3Y, 76, 64, 0x2A9D8F, &lv_font_montserrat_18);
makeKeyboardButton("CANCEL", "<CANCEL>", 380, row3Y, 80, 54, 0x5A6570, &lv_font_montserrat_16); makeKeyboardButton("CANCEL", "<CANCEL>", 380, row3Y, 80, 64, 0x5A6570, &lv_font_montserrat_16);
} }
static void drawTextEditScreen() { static void drawTextEditScreen() {
@ -839,13 +1086,21 @@ static void drawTextEditScreen() {
lv_textarea_set_one_line(gInputTextArea, true); lv_textarea_set_one_line(gInputTextArea, true);
lv_textarea_set_text(gInputTextArea, gEditValue.c_str()); lv_textarea_set_text(gInputTextArea, gEditValue.c_str());
lv_textarea_set_cursor_pos(gInputTextArea, LV_TEXTAREA_CURSOR_LAST); lv_textarea_set_cursor_pos(gInputTextArea, LV_TEXTAREA_CURSOR_LAST);
lv_textarea_set_password_mode(gInputTextArea, gEditIsPassword); lv_textarea_set_password_mode(gInputTextArea, false);
lv_textarea_set_cursor_click_pos(gInputTextArea, false);
lv_textarea_set_text_selection(gInputTextArea, false);
lv_obj_set_style_text_font(gInputTextArea, &lv_font_montserrat_18, 0); lv_obj_set_style_text_font(gInputTextArea, &lv_font_montserrat_18, 0);
lv_obj_set_style_bg_color(gInputTextArea, lv_color_hex(0x132131), 0); lv_obj_set_style_bg_color(gInputTextArea, lv_color_hex(0x132131), 0);
lv_obj_set_style_text_color(gInputTextArea, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_color(gInputTextArea, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_border_color(gInputTextArea, lv_color_hex(0x6E8AA3), 0); lv_obj_set_style_border_color(gInputTextArea, lv_color_hex(0x6E8AA3), 0);
lv_obj_set_style_border_width(gInputTextArea, 2, 0); lv_obj_set_style_border_width(gInputTextArea, 2, 0);
lv_obj_set_style_bg_opa(gInputTextArea, LV_OPA_TRANSP, LV_PART_CURSOR);
lv_obj_set_style_border_opa(gInputTextArea, LV_OPA_TRANSP, LV_PART_CURSOR);
lv_obj_set_style_outline_opa(gInputTextArea, LV_OPA_TRANSP, LV_PART_CURSOR);
lv_obj_set_style_pad_left(gInputTextArea, 12, 0);
lv_obj_set_style_pad_right(gInputTextArea, 12, 0);
lv_obj_add_state(gInputTextArea, LV_STATE_FOCUSED); lv_obj_add_state(gInputTextArea, LV_STATE_FOCUSED);
restoreTextareaFromEditValue();
lv_obj_t *panel = lv_obj_create(gRoot); lv_obj_t *panel = lv_obj_create(gRoot);
lv_obj_set_size(panel, TEXT_EDIT_PANEL_W, TEXT_EDIT_PANEL_H); lv_obj_set_size(panel, TEXT_EDIT_PANEL_W, TEXT_EDIT_PANEL_H);
@ -857,7 +1112,7 @@ static void drawTextEditScreen() {
lv_obj_set_style_border_color(panel, lv_color_hex(0x2E455A), 0); lv_obj_set_style_border_color(panel, lv_color_hex(0x2E455A), 0);
lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE); lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLLABLE);
drawTextEditorKeyboard(TEXT_EDIT_PANEL_Y + 10); drawTextEditorKeyboard(TEXT_EDIT_PANEL_Y + 8);
makeVersionTag(); makeVersionTag();
} }
@ -885,6 +1140,12 @@ static void rebuildScreen() {
case SCREEN_ACCOUNT: case SCREEN_ACCOUNT:
drawAccountScreen(); drawAccountScreen();
break; break;
case SCREEN_ACCOUNT_SUBSERVER:
drawAccountSubserverScreen();
break;
case SCREEN_ACCOUNT_SECRET:
drawAccountSecretScreen();
break;
case SCREEN_TEXT_EDIT: case SCREEN_TEXT_EDIT:
drawTextEditScreen(); drawTextEditScreen();
break; break;
@ -945,10 +1206,17 @@ static void handleAccountSwipe(SwipeDirection swipe) {
} }
} }
static void handleAccountSubscreenSwipe(SwipeDirection swipe) {
if (swipe == SWIPE_RIGHT) {
showScreen(SCREEN_ACCOUNT);
}
}
static void handleTextEditSwipe(SwipeDirection swipe) { static void handleTextEditSwipe(SwipeDirection swipe) {
if (!isTextEditKeyboardSwipeArea(gTouchStartX, gTouchStartY)) { if (!isTextEditKeyboardSwipeArea(gTouchStartX, gTouchStartY)) {
return; return;
} }
syncEditValueFromTextarea();
if (swipe == SWIPE_LEFT) { if (swipe == SWIPE_LEFT) {
gKeyboardPage = min(gKeyboardPage + 1, 1); gKeyboardPage = min(gKeyboardPage + 1, 1);
rebuildScreen(); rebuildScreen();
@ -986,6 +1254,10 @@ static void handleSwipe(SwipeDirection swipe) {
case SCREEN_ACCOUNT: case SCREEN_ACCOUNT:
handleAccountSwipe(swipe); handleAccountSwipe(swipe);
break; break;
case SCREEN_ACCOUNT_SUBSERVER:
case SCREEN_ACCOUNT_SECRET:
handleAccountSubscreenSwipe(swipe);
break;
case SCREEN_TEXT_EDIT: case SCREEN_TEXT_EDIT:
handleTextEditSwipe(swipe); handleTextEditSwipe(swipe);
break; break;
@ -1010,6 +1282,7 @@ void setup() {
gPrefs.begin("shine-nav-ui", false); gPrefs.begin("shine-nav-ui", false);
loadPrefs(); loadPrefs();
beginSavedWifi(); beginSavedWifi();
gLastWifiStatus = WiFi.status();
lv_init(); lv_init();
@ -1047,6 +1320,7 @@ void setup() {
void loop() { void loop() {
lv_timer_handler(); lv_timer_handler();
manageWifiReconnect();
if (gPendingSwipe != SWIPE_NONE) { if (gPendingSwipe != SWIPE_NONE) {
SwipeDirection swipe = gPendingSwipe; SwipeDirection swipe = gPendingSwipe;

View File

@ -1,2 +1,2 @@
client.version=1.2.143 client.version=1.2.144
server.version=1.2.135 server.version=1.2.136