Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(net): support evento v1 api #68

Merged
merged 11 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ target_compile_definitions(${PROJECT_NAME}
$<$<CONFIG:Release>:EVENTO_RELEASE>
${PLATFORM}
LOCALE_DIR="${SOURCE_LOCALE_DIR}"
EVENTO_API_V1
)

target_link_libraries(${PROJECT_NAME}
Expand Down
116 changes: 81 additions & 35 deletions src/Controller/Core/AccountManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Infrastructure/Network/ResponseStruct.h>
#include <Infrastructure/Utils/Config.h>
#include <Infrastructure/Utils/Result.h>
#include <chrono>
#include <keychain/keychain.h>
#include <optional>
#include <sast_link.h>
Expand Down Expand Up @@ -60,10 +61,15 @@
auto data = result.unwrap();

self.userInfo = data.userInfo;

self.setNetworkAccessToken(data.accessToken);
#ifdef EVENTO_API_V1
self.setKeychainAccessToken(data.accessToken);
self.expiredTime = std::chrono::system_clock::now() + std::chrono::days(30);
#else
self.setKeychainRefreshToken(data.refreshToken);
self.scheduleRenewAccessToken();
self.expiredTime = std::chrono::system_clock::now() + std::chrono::days(7);
#endif
self.setNetworkAccessToken(data.accessToken);
self.setLoginState(true);
});
}
Expand All @@ -83,17 +89,15 @@
}
co_return Ok(std::monostate{});
}(),
[weak_this = weak_from_this(), &self](Result<std::monostate> result) {
if (auto alive = weak_this.lock()) {
if (result.isErr()) {
self.setLoginState(false);
self.bridge.getMessageManager().showMessage("登录过期,请重新登录",
MessageType::Info);
return;
}
self.performGetUserInfo();
spdlog::info("refresh token success");
[&self = *this](Result<std::monostate> result) {
if (result.isErr()) {
self.setLoginState(false);
self.bridge.getMessageManager().showMessage("登录过期,请重新登录",
MessageType::Info);
return;
}
self.performGetUserInfo();
spdlog::info("refresh token success");
});
}

Expand All @@ -108,16 +112,14 @@
}
co_return result.unwrap();
}(),
[weak_this = weak_from_this(), &self](Result<UserInfoEntity> result) {
if (auto alive = weak_this.lock()) {
if (result.isErr()) {
self.setLoginState(false);
return;
}
self.userInfo = result.unwrap();
spdlog::info("get user info success");
self.setLoginState(true);
[&self = *this](Result<UserInfoEntity> result) {
if (result.isErr()) {
self.setLoginState(false);
return;
}
self.userInfo = result.unwrap();
spdlog::info("get user info success");
self.setLoginState(true);
});
}

Expand Down Expand Up @@ -146,11 +148,24 @@
if (isLogin()) {
return;
}
if (std::chrono::system_clock::now() + 15min < expiredTime) {
return;
}

#ifdef EVENTO_API_V1
if (auto token = getKeychainAccessToken()) {
spdlog::info("Token is found. Login directly!");
setNetworkAccessToken(*token);
performGetUserInfo();
}
#else
spdlog::info("Try login directly, expired time: {}", expiredTime.time_since_epoch().count());

// If the token is not expired after 15min, we don't need to login again
if (std::chrono::system_clock::now() + 15min < expiredTime) {
if (getKeychainRefreshToken()) {
performRefreshToken();
}
#endif
}

UserInfoEntity AccountManager::getUserInfo() {
Expand Down Expand Up @@ -180,17 +195,20 @@
keychain::Error err;
keychain::setPassword(package, service, userInfo.id, refreshToken, err);

if (err.code != 0) {
if (err.type != keychain::ErrorType::NoError) {
spdlog::error("Failed to save refresh token: {}", err.message);
return;
}
spdlog::info("Save refresh token success");
}

std::optional<std::string> AccountManager::getKeychainRefreshToken() const {
keychain::Error err;
auto refreshToken = keychain::getPassword(package, service, userInfo.id, err);

if (err.code != 0) {
spdlog::error("Failed to save refresh token: {}", err.message);
if (err.type != keychain::ErrorType::NoError) {
spdlog::error("Failed to get refresh token: {}", err.message);
return std::nullopt;
}

if (refreshToken.empty()) {
Expand All @@ -203,22 +221,50 @@
void AccountManager::scheduleRenewAccessToken() {
auto& self = *this;
renewAccessTokenTimer.expires_after(55min);
renewAccessTokenTimer.async_wait(
[weak_this = weak_from_this(), &self](const boost::system::error_code& ec) {
if (ec) {
spdlog::error("Failed to renew access token: {}", ec.message());
return;
}
if (auto alive = weak_this.lock()) {
self.performRefreshToken();
}
});
renewAccessTokenTimer.async_wait([&self = *this](const boost::system::error_code& ec) {
if (ec) {
spdlog::error("Failed to renew access token: {}", ec.message());
return;
}
self.performRefreshToken();
});
}

void AccountManager::setNetworkAccessToken(std::string accessToken) {
evento::networkClient()->tokenBytes = accessToken;
}

#ifdef EVENTO_API_V1
std::optional<std::string> AccountManager::getKeychainAccessToken() const {

Check warning on line 238 in src/Controller/Core/AccountManager.cc

View workflow job for this annotation

GitHub Actions / review

method 'getKeychainAccessToken' can be made static [readability-convert-member-functions-to-static]
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
keychain::Error err;
auto accessToken = keychain::getPassword(package, service, userInfo.id, err);

if (err.type != keychain::ErrorType::NoError) {
debug(), (int) err.type;
spdlog::error("Failed to get access token: {}", err.message);
return std::nullopt;
}

if (accessToken.empty()) {
return std::nullopt;
}

return accessToken;
}

void AccountManager::setKeychainAccessToken(const std::string& accessToken) const {

Check warning on line 255 in src/Controller/Core/AccountManager.cc

View workflow job for this annotation

GitHub Actions / review

method 'setKeychainAccessToken' can be made static [readability-convert-member-functions-to-static]
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
Serein207 marked this conversation as resolved.
Show resolved Hide resolved
keychain::Error err;
keychain::setPassword(package, service, userInfo.id, accessToken, err);

if (err.type != keychain::ErrorType::NoError) {
debug(), (int) err.type;
spdlog::error("Failed to save access token: {}", err.message);
return;
}
spdlog::info("Save access token success");
}
#endif

void AccountManager::setLoginState(bool newState) {
auto& self = *this;
if (loginState != newState) {
Expand Down
12 changes: 11 additions & 1 deletion src/Controller/Core/AccountManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ class AccountManager : private GlobalAgent<AccountManagerBridge>,
net::steady_timer renewAccessTokenTimer;

static const inline std::string package = "org.sast.evento";
static const inline std::string service = "refresh-token";
static const inline std::string service =
#ifdef EVENTO_API_V1
"access-token";
#else
"refresh-token";
#endif

public:
AccountManager(slint::ComponentHandle<UiEntryName> uiEntry, UiBridge& bridge);
Expand All @@ -45,6 +50,11 @@ class AccountManager : private GlobalAgent<AccountManagerBridge>,

static void setNetworkAccessToken(std::string accessToken);

#ifdef EVENTO_API_V1
[[nodiscard]] std::optional<std::string> getKeychainAccessToken() const;
void setKeychainAccessToken(const std::string& accessToken) const;
#endif

void setLoginState(bool newState);
void onStateChanged();

Expand Down
70 changes: 35 additions & 35 deletions src/Controller/View/HistoryPage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <Controller/View/HistoryPage.h>
#include <Infrastructure/Network/NetworkClient.h>
#include <Infrastructure/Network/ResponseStruct.h>
#include <ranges>
#include <slint.h>
#include <spdlog/spdlog.h>

Expand Down Expand Up @@ -38,43 +37,44 @@ void HistoryPage::loadHistoryEvents(int page, int size) {
auto& self = *this;
self->set_state(PageState::Loading);

executor()->asyncExecute(
networkClient()->getHistoryEventList(page, size),
[&self = *this, this](Result<EventQueryRes> result) {
if (result.isErr()) {
self->set_state(PageState::Error);
self.bridge.getMessageManager().showMessage(result.unwrapErr().what(),
MessageType::Error);
return;
}
executor()->asyncExecute(loadHistoryEventsTask(page, size),
[&self = *this, this](Result<std::vector<EventFeedbackStruct>> result) {
if (result.isErr()) {
return;
}

auto res = result.unwrap();
self->set_total(res.total);
self->set_events(convert::from(res.elements));

feedbacks.clear();
auto feedbackSize = res.elements.size();
auto list = result.unwrap();
self->set_total(static_cast<int>(list.size()));
self->set_models(
std::make_shared<slint::VectorModel<EventFeedbackStruct>>(
list));
self->set_state(PageState::Normal);
});
}

auto trans = [](const auto& e) { return e.id; };
for (const auto& eventId : res.elements | std::views::transform(trans)) {
executor()->asyncExecute(
networkClient()->getUserFeedback(eventId, 0min),
[&self = *this, feedbackSize](Result<std::optional<FeedbackEntity>> result) {
if (result.isErr()) {
spdlog::warn("feedback load failed: {}", result.unwrapErr().what());
self.feedbacks.emplace_back(FeedbackStruct{});
} else {
self.feedbacks.emplace_back(convert::from(result.unwrap()));
}
Task<Result<std::vector<EventFeedbackStruct>>> HistoryPage::loadHistoryEventsTask(int page,
int size) {
auto& self = *this;
auto historyEventsRes = co_await networkClient()->getHistoryEventList(page, size);
if (historyEventsRes.isErr()) {
slint::invoke_from_event_loop([&self = *this] { self->set_state(PageState::Error); });
co_return historyEventsRes.unwrapErr();
}

if (self.feedbacks.size() == feedbackSize) {
self->set_feedbacks(std::make_shared<slint::VectorModel<FeedbackStruct>>(
self.feedbacks));
self->set_state(PageState::Normal);
}
});
}
});
auto historyEvents = historyEventsRes.unwrap();
std::vector<EventFeedbackStruct> res;
for (auto const& event : historyEvents.elements) {
auto feedbackRes = co_await networkClient()->getUserFeedback(event.id);
if (feedbackRes.isErr()) {
spdlog::warn("feedback load failed: {}", feedbackRes.unwrapErr().what());
self.bridge.getMessageManager().showMessage(feedbackRes.unwrapErr().what(),
MessageType::Error);
res.emplace_back(convert::from(event), FeedbackStruct{});
} else {
res.emplace_back(convert::from(event), convert::from(feedbackRes.unwrap()));
}
}
co_return res;
}

void HistoryPage::feedbackEvent(int eventId, int rating, std::string content) {
Expand Down
5 changes: 4 additions & 1 deletion src/Controller/View/HistoryPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <Controller/Core/BasicView.h>
#include <Controller/Core/GlobalAgent.hh>
#include <Controller/Core/UiBase.h>
#include <Infrastructure/Utils/Result.h>
#include <vector>

EVENTO_UI_START
Expand All @@ -18,9 +19,11 @@ class HistoryPage : public BasicView, private GlobalAgent<HistoryPageBridge> {

void loadHistoryEvents(int page, int size);

Task<Result<std::vector<EventFeedbackStruct>>> loadHistoryEventsTask(int page, int size);

void feedbackEvent(int eventId, int rating, std::string content);

std::vector<FeedbackStruct> feedbacks;
std::vector<EventFeedbackStruct> eventFeedbacks;
};

EVENTO_UI_END
1 change: 1 addition & 0 deletions src/Controller/View/MyEventPage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ void MyEventPage::refreshUiModel(Result<EventQueryRes> result) {
self->set_not_started_model(convert::from(models[(int) EventState::SigningUp]));
self->set_active_model(convert::from(models[(int) EventState::Active]));
self->set_completed_model(convert::from(models[(int) EventState::Completed]));
self->set_state(PageState::Normal);
}

EVENTO_UI_END
2 changes: 1 addition & 1 deletion src/Infrastructure/Network/Api/Evento.hh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct Evento {
std::format("{}{}{}",
url.path(),
url.has_query() ? "?" : "",
url.query()),
url.encoded_query().data()),
11};

req.set(http::field::host, url.host());
Expand Down
Loading
Loading