Fix: Prompt to connect to WiFi on GitHub Sync
This commit is contained in:
+21
-18
@@ -4,6 +4,7 @@
|
|||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <Logging.h>
|
||||||
|
|
||||||
#define GH_PREFS_NS "github_sync"
|
#define GH_PREFS_NS "github_sync"
|
||||||
#define GH_KEY_USER "username"
|
#define GH_KEY_USER "username"
|
||||||
@@ -44,15 +45,15 @@ bool GitHubSync::isConfigured() {
|
|||||||
|
|
||||||
const char* GitHubSync::resultMessage(GitHubSyncResult r) {
|
const char* GitHubSync::resultMessage(GitHubSyncResult r) {
|
||||||
switch (r) {
|
switch (r) {
|
||||||
case GitHubSyncResult::OK: return "GitHub sync complete";
|
case GitHubSyncResult::OK: return "Sync complete";
|
||||||
case GitHubSyncResult::NOT_CONFIGURED: return "GitHub sync: not configured";
|
case GitHubSyncResult::NOT_CONFIGURED: return "Not configured";
|
||||||
case GitHubSyncResult::NO_WIFI: return "GitHub sync: no WiFi";
|
case GitHubSyncResult::NO_WIFI: return "No WiFi";
|
||||||
case GitHubSyncResult::AUTH_ERROR: return "GitHub sync: auth failed (check PAT)";
|
case GitHubSyncResult::AUTH_ERROR: return "Auth failed (check PAT)";
|
||||||
case GitHubSyncResult::REPO_NOT_FOUND: return "GitHub sync: repo not found";
|
case GitHubSyncResult::REPO_NOT_FOUND: return "Repo not found";
|
||||||
case GitHubSyncResult::API_ERROR: return "GitHub sync: API error";
|
case GitHubSyncResult::API_ERROR: return "API error";
|
||||||
case GitHubSyncResult::SD_ERROR: return "GitHub sync: SD card error";
|
case GitHubSyncResult::SD_ERROR: return "SD card error";
|
||||||
case GitHubSyncResult::PARSE_ERROR: return "GitHub sync: bad API response";
|
case GitHubSyncResult::PARSE_ERROR: return "Bad API response";
|
||||||
default: return "GitHub sync: unknown error";
|
default: return "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +93,7 @@ bool GitHubSync::fetchFileList(const GitHubSyncConfig &cfg, std::string &outJson
|
|||||||
http.addHeader("User-Agent", "CrossPoint-X4");
|
http.addHeader("User-Agent", "CrossPoint-X4");
|
||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
|
LOG_INF("SYNC", "fetchFileList HTTP code: %d", code);
|
||||||
if (code == 401 || code == 403) { http.end(); err = GitHubSyncResult::AUTH_ERROR; return false; }
|
if (code == 401 || code == 403) { http.end(); err = GitHubSyncResult::AUTH_ERROR; return false; }
|
||||||
if (code == 404) { http.end(); err = GitHubSyncResult::REPO_NOT_FOUND; return false; }
|
if (code == 404) { http.end(); err = GitHubSyncResult::REPO_NOT_FOUND; return false; }
|
||||||
if (code != 200) { http.end(); err = GitHubSyncResult::API_ERROR; return false; }
|
if (code != 200) { http.end(); err = GitHubSyncResult::API_ERROR; return false; }
|
||||||
@@ -101,21 +103,19 @@ bool GitHubSync::fetchFileList(const GitHubSyncConfig &cfg, std::string &outJson
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitHubSync::downloadFile(const GitHubSyncConfig &cfg, const std::string &path, const std::string &sha, GitHubSyncResult &err) {
|
bool GitHubSync::downloadFile(const GitHubSyncConfig &cfg, const std::string &downloadUrl, const std::string &name, const std::string &sha, GitHubSyncResult &err) {
|
||||||
std::string url = std::string(GH_API_BASE) + "/repos/" + cfg.username + "/" +
|
|
||||||
cfg.repo + "/contents/" + path + "?ref=" + cfg.branch;
|
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
http.begin(url.c_str());
|
http.begin(downloadUrl.c_str());
|
||||||
http.addHeader("Authorization", ("token " + cfg.pat).c_str());
|
http.addHeader("Authorization", ("token " + cfg.pat).c_str());
|
||||||
http.addHeader("Accept", "application/vnd.github.v3.raw");
|
|
||||||
http.addHeader("User-Agent", "CrossPoint-X4");
|
http.addHeader("User-Agent", "CrossPoint-X4");
|
||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
|
LOG_INF("SYNC", "downloadFile '%s' HTTP code: %d", name.c_str(), code);
|
||||||
if (code == 401 || code == 403) { http.end(); err = GitHubSyncResult::AUTH_ERROR; return false; }
|
if (code == 401 || code == 403) { http.end(); err = GitHubSyncResult::AUTH_ERROR; return false; }
|
||||||
if (code != 200) { http.end(); err = GitHubSyncResult::API_ERROR; return false; }
|
if (code != 200) { http.end(); err = GitHubSyncResult::API_ERROR; return false; }
|
||||||
|
|
||||||
std::string destPath = (path == GH_SLEEP_BMP) ? GH_SLEEP_PATH : std::string(GH_BOOKS_DIR) + path;
|
std::string destPath = (name == GH_SLEEP_BMP) ? GH_SLEEP_PATH : std::string(GH_BOOKS_DIR) + name;
|
||||||
|
LOG_INF("SYNC", "Writing to SD path: '%s'", destPath.c_str());
|
||||||
|
|
||||||
File f = SD.open(destPath.c_str(), FILE_WRITE);
|
File f = SD.open(destPath.c_str(), FILE_WRITE);
|
||||||
if (!f) { http.end(); err = GitHubSyncResult::SD_ERROR; return false; }
|
if (!f) { http.end(); err = GitHubSyncResult::SD_ERROR; return false; }
|
||||||
@@ -137,7 +137,7 @@ bool GitHubSync::downloadFile(const GitHubSyncConfig &cfg, const std::string &pa
|
|||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
http.end();
|
http.end();
|
||||||
saveLocalSha(path, sha);
|
saveLocalSha(name, sha);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +159,7 @@ GitHubSyncResult GitHubSync::sync() {
|
|||||||
std::string type = file["type"].as<const char*>();
|
std::string type = file["type"].as<const char*>();
|
||||||
std::string name = file["name"].as<const char*>();
|
std::string name = file["name"].as<const char*>();
|
||||||
std::string sha = file["sha"].as<const char*>();
|
std::string sha = file["sha"].as<const char*>();
|
||||||
|
std::string downloadUrl = file["download_url"].as<const char*>();
|
||||||
|
|
||||||
if (type != "file") continue;
|
if (type != "file") continue;
|
||||||
|
|
||||||
@@ -166,11 +167,13 @@ GitHubSyncResult GitHubSync::sync() {
|
|||||||
bool isSleep = (name == GH_SLEEP_BMP);
|
bool isSleep = (name == GH_SLEEP_BMP);
|
||||||
|
|
||||||
if (!isEpub && !isSleep) continue;
|
if (!isEpub && !isSleep) continue;
|
||||||
|
if (downloadUrl.empty()) continue;
|
||||||
|
|
||||||
std::string localSha = loadLocalSha(name);
|
std::string localSha = loadLocalSha(name);
|
||||||
if (localSha == sha) continue;
|
if (localSha == sha) continue;
|
||||||
|
|
||||||
if (!downloadFile(cfg, name, sha, err)) return err;
|
LOG_INF("SYNC", "Downloading '%s'", name.c_str());
|
||||||
|
if (!downloadFile(cfg, downloadUrl, name, sha, err)) return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GitHubSyncResult::OK;
|
return GitHubSyncResult::OK;
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static bool fetchFileList(const GitHubSyncConfig &cfg, std::string &outJson, GitHubSyncResult &err);
|
static bool fetchFileList(const GitHubSyncConfig &cfg, std::string &outJson, GitHubSyncResult &err);
|
||||||
static bool downloadFile(const GitHubSyncConfig &cfg, const std::string &path, const std::string &sha, GitHubSyncResult &err);
|
static bool downloadFile(const GitHubSyncConfig &cfg, const std::string &downloadUrl, const std::string &name, const std::string &sha, GitHubSyncResult &err);
|
||||||
static std::string loadLocalSha(const std::string &filename);
|
static std::string loadLocalSha(const std::string &filename);
|
||||||
static void saveLocalSha(const std::string &filename, const std::string &sha);
|
static void saveLocalSha(const std::string &filename, const std::string &sha);
|
||||||
static std::string shaFilePath(const std::string &filename);
|
static std::string shaFilePath(const std::string &filename);
|
||||||
|
|||||||
@@ -2,19 +2,26 @@
|
|||||||
|
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "activities/util/KeyboardEntryActivity.h"
|
#include "activities/util/KeyboardEntryActivity.h"
|
||||||
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int MENU_ITEMS = 5;
|
constexpr int MENU_ITEMS = 7;
|
||||||
|
constexpr int WIFI_STATUS_INDEX = 4;
|
||||||
|
constexpr int SYNC_NOW_INDEX = 5;
|
||||||
|
constexpr int SAVE_INDEX = 6;
|
||||||
const char* menuNames[MENU_ITEMS] = {
|
const char* menuNames[MENU_ITEMS] = {
|
||||||
"Username",
|
"Username",
|
||||||
"Token (PAT)",
|
"Token (PAT)",
|
||||||
"Repo",
|
"Repo",
|
||||||
"Branch",
|
"Branch",
|
||||||
|
"WiFi",
|
||||||
|
"Sync Now",
|
||||||
"Save"
|
"Save"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -33,6 +40,19 @@ std::string GitHubSyncSettingsActivity::getMasked(const std::string &s) const {
|
|||||||
return s.empty() ? "" : "••••••••";
|
return s.empty() ? "" : "••••••••";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GitHubSyncSettingsActivity::doSync() {
|
||||||
|
GitHubSyncConfig cfg;
|
||||||
|
GitHubSync::loadConfig(cfg);
|
||||||
|
LOG_INF("SYNC", "user='%s' repo='%s' branch='%s' pat_len=%d",
|
||||||
|
cfg.username.c_str(), cfg.repo.c_str(), cfg.branch.c_str(), (int)cfg.pat.size());
|
||||||
|
syncStatus = "Syncing...";
|
||||||
|
requestUpdate();
|
||||||
|
GitHubSyncResult result = GitHubSync::sync();
|
||||||
|
syncStatus = GitHubSync::resultMessage(result);
|
||||||
|
LOG_INF("SYNC", "result: %s", syncStatus.c_str());
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
void GitHubSyncSettingsActivity::handleSelection() {
|
void GitHubSyncSettingsActivity::handleSelection() {
|
||||||
GitHubSyncConfig cfg;
|
GitHubSyncConfig cfg;
|
||||||
GitHubSync::loadConfig(cfg);
|
GitHubSync::loadConfig(cfg);
|
||||||
@@ -88,7 +108,25 @@ void GitHubSyncSettingsActivity::handleSelection() {
|
|||||||
GitHubSync::saveConfig(c);
|
GitHubSync::saveConfig(c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (selectedIndex == 4) {
|
} else if (selectedIndex == SYNC_NOW_INDEX) {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
syncStatus = "Connecting to WiFi...";
|
||||||
|
requestUpdate();
|
||||||
|
startActivityForResult(
|
||||||
|
std::make_unique<WifiSelectionActivity>(renderer, mappedInput),
|
||||||
|
[this](const ActivityResult &result) {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
syncStatus = "No WiFi — sync cancelled";
|
||||||
|
requestUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doSync();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
doSync();
|
||||||
|
}
|
||||||
|
requestUpdate();
|
||||||
|
} else if (selectedIndex == SAVE_INDEX) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,11 +144,15 @@ void GitHubSyncSettingsActivity::loop() {
|
|||||||
|
|
||||||
buttonNavigator.onNext([this] {
|
buttonNavigator.onNext([this] {
|
||||||
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
||||||
|
if (selectedIndex == WIFI_STATUS_INDEX)
|
||||||
|
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonNavigator.onPrevious([this] {
|
buttonNavigator.onPrevious([this] {
|
||||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||||
|
if (selectedIndex == WIFI_STATUS_INDEX)
|
||||||
|
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -140,6 +182,9 @@ void GitHubSyncSettingsActivity::render(RenderLock &&) {
|
|||||||
if (index == 1) return cfg.pat.empty() ? "(not set)" : getMasked(cfg.pat);
|
if (index == 1) return cfg.pat.empty() ? "(not set)" : getMasked(cfg.pat);
|
||||||
if (index == 2) return cfg.repo.empty() ? "xteink" : cfg.repo;
|
if (index == 2) return cfg.repo.empty() ? "xteink" : cfg.repo;
|
||||||
if (index == 3) return cfg.branch.empty() ? "main" : cfg.branch;
|
if (index == 3) return cfg.branch.empty() ? "main" : cfg.branch;
|
||||||
|
if (index == WIFI_STATUS_INDEX)
|
||||||
|
return WiFi.status() == WL_CONNECTED ? "Connected" : "Not connected";
|
||||||
|
if (index == SYNC_NOW_INDEX) return syncStatus;
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ public:
|
|||||||
private:
|
private:
|
||||||
ButtonNavigator buttonNavigator;
|
ButtonNavigator buttonNavigator;
|
||||||
int selectedIndex = 0;
|
int selectedIndex = 0;
|
||||||
|
std::string syncStatus;
|
||||||
|
|
||||||
static constexpr int MENU_ITEMS = 5;
|
static constexpr int MENU_ITEMS = 7;
|
||||||
|
|
||||||
void handleSelection();
|
void handleSelection();
|
||||||
|
void doSync();
|
||||||
std::string getMasked(const std::string& s) const;
|
std::string getMasked(const std::string& s) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ def prompt_for_upload_port(existing_port: str | None) -> str | None:
|
|||||||
|
|
||||||
current_ports = list_likely_serial_ports()
|
current_ports = list_likely_serial_ports()
|
||||||
if current_ports:
|
if current_ports:
|
||||||
entered = input(" Upload port (recommended, e.g. /dev/cu.usbmodemXXXX; Enter for auto-detect): ").strip()
|
entered = input(" Upload port (recommended, e.g. /dev/cu.usbmodemXXXX; Press ENTER for auto-detect): ").strip()
|
||||||
if entered:
|
if entered:
|
||||||
return entered
|
return entered
|
||||||
warn("Proceeding with auto-detect. This may choose a non-USB port (like Bluetooth).")
|
warn("Proceeding with auto-detect. This may choose a non-USB port (like Bluetooth).")
|
||||||
|
|||||||
Reference in New Issue
Block a user