about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-05-06 12:24:01 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-05-06 12:24:01 -0400
commitd20664f90a84da691baeae5fb55bb28f409ed577 (patch)
tree6a259fb645cda39aa479849e6881e78f91924fad
parentd7212d755dca7f4fd99cf4b775cd0d372d7bcbb2 (diff)
downloadlingo-ap-tracker-d20664f90a84da691baeae5fb55bb28f409ed577.tar.gz
lingo-ap-tracker-d20664f90a84da691baeae5fb55bb28f409ed577.tar.bz2
lingo-ap-tracker-d20664f90a84da691baeae5fb55bb28f409ed577.zip
Automatically check for updates
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/tracker_config.cpp4
-rw-r--r--src/tracker_config.h2
-rw-r--r--src/tracker_frame.cpp101
-rw-r--r--src/tracker_frame.h4
-rw-r--r--src/version.h40
6 files changed, 149 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index af3b0ba..6b36d09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -43,4 +43,4 @@ add_executable(lingo_ap_tracker
43) 43)
44set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) 44set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20)
45set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) 45set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON)
46target_link_libraries(lingo_ap_tracker PRIVATE OpenSSL::SSL OpenSSL::Crypto wx::core wx::base yaml-cpp) 46target_link_libraries(lingo_ap_tracker PRIVATE OpenSSL::SSL OpenSSL::Crypto wx::core wx::base wx::net yaml-cpp)
diff --git a/src/tracker_config.cpp b/src/tracker_config.cpp index 96bb60a..c09d241 100644 --- a/src/tracker_config.cpp +++ b/src/tracker_config.cpp
@@ -12,6 +12,8 @@ void TrackerConfig::Load() {
12 ap_server = file["ap_server"].as<std::string>(); 12 ap_server = file["ap_server"].as<std::string>();
13 ap_player = file["ap_player"].as<std::string>(); 13 ap_player = file["ap_player"].as<std::string>();
14 ap_password = file["ap_password"].as<std::string>(); 14 ap_password = file["ap_password"].as<std::string>();
15 asked_to_check_for_updates = file["asked_to_check_for_updates"].as<bool>();
16 should_check_for_updates = file["should_check_for_updates"].as<bool>();
15 } catch (const std::exception&) { 17 } catch (const std::exception&) {
16 // It's fine if the file can't be loaded. 18 // It's fine if the file can't be loaded.
17 } 19 }
@@ -22,6 +24,8 @@ void TrackerConfig::Save() {
22 output["ap_server"] = ap_server; 24 output["ap_server"] = ap_server;
23 output["ap_player"] = ap_player; 25 output["ap_player"] = ap_player;
24 output["ap_password"] = ap_password; 26 output["ap_password"] = ap_password;
27 output["asked_to_check_for_updates"] = asked_to_check_for_updates;
28 output["should_check_for_updates"] = should_check_for_updates;
25 29
26 std::ofstream filewriter(CONFIG_FILE_NAME); 30 std::ofstream filewriter(CONFIG_FILE_NAME);
27 filewriter << output; 31 filewriter << output;
diff --git a/src/tracker_config.h b/src/tracker_config.h index e2e6cd7..b9460d2 100644 --- a/src/tracker_config.h +++ b/src/tracker_config.h
@@ -12,6 +12,8 @@ class TrackerConfig {
12 std::string ap_server; 12 std::string ap_server;
13 std::string ap_player; 13 std::string ap_player;
14 std::string ap_password; 14 std::string ap_password;
15 bool asked_to_check_for_updates = false;
16 bool should_check_for_updates = false;
15}; 17};
16 18
17TrackerConfig& GetTrackerConfig(); 19TrackerConfig& GetTrackerConfig();
diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index 2a862a5..0308886 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp
@@ -1,11 +1,17 @@
1#include "tracker_frame.h" 1#include "tracker_frame.h"
2 2
3#include <wx/webrequest.h>
4
5#include <nlohmann/json.hpp>
6#include <sstream>
7
3#include "ap_state.h" 8#include "ap_state.h"
4#include "connection_dialog.h" 9#include "connection_dialog.h"
5#include "tracker_config.h" 10#include "tracker_config.h"
6#include "tracker_panel.h" 11#include "tracker_panel.h"
12#include "version.h"
7 13
8enum TrackerFrameIds { ID_CONNECT = 1 }; 14enum TrackerFrameIds { ID_CONNECT = 1, ID_CHECK_FOR_UPDATES = 2 };
9 15
10wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); 16wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent);
11wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); 17wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent);
@@ -25,6 +31,7 @@ TrackerFrame::TrackerFrame()
25 31
26 wxMenu *menuHelp = new wxMenu(); 32 wxMenu *menuHelp = new wxMenu();
27 menuHelp->Append(wxID_ABOUT); 33 menuHelp->Append(wxID_ABOUT);
34 menuHelp->Append(ID_CHECK_FOR_UPDATES, "Check for Updates");
28 35
29 wxMenuBar *menuBar = new wxMenuBar(); 36 wxMenuBar *menuBar = new wxMenuBar();
30 menuBar->Append(menuFile, "&File"); 37 menuBar->Append(menuFile, "&File");
@@ -38,10 +45,30 @@ TrackerFrame::TrackerFrame()
38 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); 45 Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT);
39 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); 46 Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT);
40 Bind(wxEVT_MENU, &TrackerFrame::OnConnect, this, ID_CONNECT); 47 Bind(wxEVT_MENU, &TrackerFrame::OnConnect, this, ID_CONNECT);
48 Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this,
49 ID_CHECK_FOR_UPDATES);
41 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); 50 Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this);
42 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); 51 Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this);
43 52
44 tracker_panel_ = new TrackerPanel(this); 53 tracker_panel_ = new TrackerPanel(this);
54
55 if (!GetTrackerConfig().asked_to_check_for_updates) {
56 GetTrackerConfig().asked_to_check_for_updates = true;
57
58 if (wxMessageBox(
59 "Check for updates automatically when the tracker is opened?",
60 "Lingo AP Tracker", wxYES_NO) == wxYES) {
61 GetTrackerConfig().should_check_for_updates = true;
62 } else {
63 GetTrackerConfig().should_check_for_updates = false;
64 }
65
66 GetTrackerConfig().Save();
67 }
68
69 if (GetTrackerConfig().should_check_for_updates) {
70 CheckForUpdates(/*manual=*/false);
71 }
45} 72}
46 73
47void TrackerFrame::SetStatusMessage(std::string message) { 74void TrackerFrame::SetStatusMessage(std::string message) {
@@ -56,8 +83,12 @@ void TrackerFrame::UpdateIndicators() {
56} 83}
57 84
58void TrackerFrame::OnAbout(wxCommandEvent &event) { 85void TrackerFrame::OnAbout(wxCommandEvent &event) {
59 wxMessageBox("Lingo Archipelago Tracker by hatkirby", 86 std::ostringstream message_text;
60 "About lingo-ap-tracker", wxOK | wxICON_INFORMATION); 87 message_text << "Lingo Archipelago Tracker " << kTrackerVersion
88 << " by hatkirby";
89
90 wxMessageBox(message_text.str(), "About lingo-ap-tracker",
91 wxOK | wxICON_INFORMATION);
61} 92}
62 93
63void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); } 94void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); }
@@ -76,6 +107,10 @@ void TrackerFrame::OnConnect(wxCommandEvent &event) {
76 } 107 }
77} 108}
78 109
110void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) {
111 CheckForUpdates(/*manual=*/true);
112}
113
79void TrackerFrame::OnStateChanged(wxCommandEvent &event) { 114void TrackerFrame::OnStateChanged(wxCommandEvent &event) {
80 tracker_panel_->UpdateIndicators(); 115 tracker_panel_->UpdateIndicators();
81 Refresh(); 116 Refresh();
@@ -84,3 +119,63 @@ void TrackerFrame::OnStateChanged(wxCommandEvent &event) {
84void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { 119void TrackerFrame::OnStatusChanged(wxCommandEvent &event) {
85 SetStatusText(event.GetString()); 120 SetStatusText(event.GetString());
86} 121}
122
123void TrackerFrame::CheckForUpdates(bool manual) {
124 wxWebRequest request = wxWebSession::GetDefault().CreateRequest(
125 this,
126 "https://api.github.com/repos/hatkirby/lingo-ap-tracker/"
127 "releases?per_page=8");
128
129 if (!request.IsOk()) {
130 if (manual) {
131 wxMessageBox("Could not check for updates.", "Error",
132 wxOK | wxICON_ERROR);
133 } else {
134 SetStatusText("Could not check for updates.");
135 }
136
137 return;
138 }
139
140 Bind(wxEVT_WEBREQUEST_STATE, [this, manual](wxWebRequestEvent &evt) {
141 if (evt.GetState() == wxWebRequest::State_Completed) {
142 std::string response = evt.GetResponse().AsString().ToStdString();
143 nlohmann::json parsed_response = nlohmann::json::parse(response);
144
145 if (parsed_response.is_array() && !parsed_response.empty()) {
146 // This will parse to 0.0.0 if it's invalid, which will always be older
147 // than our current version.
148 Version latest_version(
149 parsed_response[0]["tag_name"].get<std::string>());
150 if (kTrackerVersion < latest_version) {
151 std::ostringstream message_text;
152 message_text << "There is a newer version of Lingo AP Tracker "
153 "available. You have "
154 << kTrackerVersion << ", and the latest version is "
155 << latest_version << ". Would you like to update?";
156
157 if (wxMessageBox(message_text.str(), "Update available", wxYES_NO) ==
158 wxYES) {
159 wxLaunchDefaultBrowser(
160 parsed_response[0]["html_url"].get<std::string>());
161 }
162 } else if (manual) {
163 wxMessageBox("Lingo AP Tracker is up to date!", "Lingo AP Tracker",
164 wxOK);
165 }
166 } else if (manual) {
167 wxMessageBox("Lingo AP Tracker is up to date!", "Lingo AP Tracker",
168 wxOK);
169 }
170 } else if (evt.GetState() == wxWebRequest::State_Failed) {
171 if (manual) {
172 wxMessageBox("Could not check for updates.", "Error",
173 wxOK | wxICON_ERROR);
174 } else {
175 SetStatusText("Could not check for updates.");
176 }
177 }
178 });
179
180 request.Start();
181}
diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 6f4e4fc..f3470fd 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h
@@ -24,9 +24,13 @@ class TrackerFrame : public wxFrame {
24 void OnExit(wxCommandEvent &event); 24 void OnExit(wxCommandEvent &event);
25 void OnAbout(wxCommandEvent &event); 25 void OnAbout(wxCommandEvent &event);
26 void OnConnect(wxCommandEvent &event); 26 void OnConnect(wxCommandEvent &event);
27 void OnCheckForUpdates(wxCommandEvent &event);
28
27 void OnStateChanged(wxCommandEvent &event); 29 void OnStateChanged(wxCommandEvent &event);
28 void OnStatusChanged(wxCommandEvent &event); 30 void OnStatusChanged(wxCommandEvent &event);
29 31
32 void CheckForUpdates(bool manual);
33
30 TrackerPanel *tracker_panel_; 34 TrackerPanel *tracker_panel_;
31}; 35};
32 36
diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..c45ff53 --- /dev/null +++ b/src/version.h
@@ -0,0 +1,40 @@
1#ifndef VERSION_H_C757E53C
2#define VERSION_H_C757E53C
3
4#include <iostream>
5#include <regex>
6
7struct Version {
8 int major = 0;
9 int minor = 0;
10 int revision = 0;
11
12 constexpr Version(int major_arg, int minor_arg, int rev_arg)
13 : major(major_arg), minor(minor_arg), revision(rev_arg) {}
14
15 Version(const std::string& ver_str) {
16 const std::regex version_regex("v([0-9]*)\.([0-9]*)\.([0-9]*)");
17 std::smatch version_match;
18
19 if (std::regex_match(ver_str, version_match, version_regex)) {
20 major = std::atoi(version_match[1].str().c_str());
21 minor = std::atoi(version_match[2].str().c_str());
22 revision = std::atoi(version_match[3].str().c_str());
23 }
24 }
25
26 bool operator<(const Version& rhs) const {
27 return (major < rhs.major) ||
28 (major == rhs.major &&
29 (minor < rhs.minor ||
30 (minor == rhs.minor && revision < rhs.revision)));
31 }
32};
33
34std::ostream& operator<<(std::ostream& out, const Version& ver) {
35 return out << "v" << ver.major << "." << ver.minor << "." << ver.revision;
36}
37
38constexpr const Version kTrackerVersion = Version(0, 1, 0);
39
40#endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file