This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
The-Powder-Toy/src/gui/search/SearchModel.cpp
Tamás Bálint Misius 4f0c365e05
Preprocessor purge round 19: Split and minimize usage of Config.h
Also mostly banish it from other headers, and shuffle standard header includes to minimize cross-contamination between headers.
2023-01-27 09:27:32 +01:00

375 lines
8.3 KiB
C++

#include "SearchModel.h"
#include "SearchView.h"
#include "Format.h"
#include "client/SaveInfo.h"
#include "client/Client.h"
#include "common/tpt-minmax.h"
#include <thread>
#include <cmath>
SearchModel::SearchModel():
loadedSave(NULL),
currentSort("best"),
currentPage(1),
resultCount(0),
showOwn(false),
showFavourite(false),
showTags(true)
{
}
void SearchModel::SetShowTags(bool show)
{
showTags = show;
}
bool SearchModel::GetShowTags()
{
return showTags;
}
void SearchModel::BeginSearchSaves(int start, int count, String query, ByteString sort, ByteString category)
{
lastError = "";
resultCount = 0;
ByteStringBuilder urlStream;
ByteString data;
urlStream << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
if(query.length() || sort.length())
{
urlStream << "&Search_Query=";
if(query.length())
urlStream << format::URLEncode(query.ToUtf8());
if(sort == "date")
{
if(query.length())
urlStream << format::URLEncode(" ");
urlStream << format::URLEncode("sort:") << format::URLEncode(sort);
}
}
if(category.length())
{
urlStream << "&Category=" << format::URLEncode(category);
}
searchSaves = std::make_unique<http::Request>(urlStream.Build());
auto authUser = Client::Ref().GetAuthUser();
if (authUser.UserID)
{
searchSaves->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
}
searchSaves->Start();
}
std::vector<SaveInfo *> SearchModel::EndSearchSaves()
{
std::vector<SaveInfo *> saveArray;
auto [ dataStatus, data ] = searchSaves->Finish();
searchSaves.reset();
auto &client = Client::Ref();
client.ParseServerReturn(data, dataStatus, true);
if (dataStatus == 200 && data.size())
{
try
{
std::istringstream dataStream(data);
Json::Value objDocument;
dataStream >> objDocument;
resultCount = objDocument["Count"].asInt();
Json::Value savesArray = objDocument["Saves"];
for (Json::UInt j = 0; j < savesArray.size(); j++)
{
int tempID = savesArray[j]["ID"].asInt();
int tempCreatedDate = savesArray[j]["Created"].asInt();
int tempUpdatedDate = savesArray[j]["Updated"].asInt();
int tempScoreUp = savesArray[j]["ScoreUp"].asInt();
int tempScoreDown = savesArray[j]["ScoreDown"].asInt();
ByteString tempUsername = savesArray[j]["Username"].asString();
String tempName = ByteString(savesArray[j]["Name"].asString()).FromUtf8();
int tempVersion = savesArray[j]["Version"].asInt();
bool tempPublished = savesArray[j]["Published"].asBool();
SaveInfo * tempSaveInfo = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, tempScoreDown, tempUsername, tempName);
tempSaveInfo->Version = tempVersion;
tempSaveInfo->SetPublished(tempPublished);
saveArray.push_back(tempSaveInfo);
}
}
catch (std::exception &e)
{
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
}
}
else
{
lastError = client.GetLastError();
}
return saveArray;
}
void SearchModel::BeginGetTags(int start, int count, String query)
{
lastError = "";
ByteStringBuilder urlStream;
urlStream << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count;
if(query.length())
{
urlStream << "&Search_Query=";
if(query.length())
urlStream << format::URLEncode(query.ToUtf8());
}
getTags = std::make_unique<http::Request>(urlStream.Build());
getTags->Start();
}
std::vector<std::pair<ByteString, int>> SearchModel::EndGetTags()
{
std::vector<std::pair<ByteString, int>> tagArray;
auto [ dataStatus, data ] = getTags->Finish();
getTags.reset();
if(dataStatus == 200 && data.size())
{
try
{
std::istringstream dataStream(data);
Json::Value objDocument;
dataStream >> objDocument;
Json::Value tagsArray = objDocument["Tags"];
for (Json::UInt j = 0; j < tagsArray.size(); j++)
{
int tagCount = tagsArray[j]["Count"].asInt();
ByteString tag = tagsArray[j]["Tag"].asString();
tagArray.push_back(std::pair<ByteString, int>(tag, tagCount));
}
}
catch (std::exception & e)
{
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
}
}
else
{
lastError = http::StatusText(dataStatus);
}
return tagArray;
}
bool SearchModel::UpdateSaveList(int pageNumber, String query)
{
//Threading
if (!searchSaves)
{
lastQuery = query;
lastError = "";
saveListLoaded = false;
saveList.clear();
//resultCount = 0;
currentPage = pageNumber;
if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "")
SetShowTags(true);
else
SetShowTags(false);
notifySaveListChanged();
notifyTagListChanged();
notifyPageChanged();
selected.clear();
notifySelectedChanged();
if (GetShowTags() && !tagList.size() && !getTags)
{
BeginGetTags(0, 24, "");
}
ByteString category = "";
if(showFavourite)
category = "Favourites";
if(showOwn && Client::Ref().GetAuthUser().UserID)
category = "by:"+Client::Ref().GetAuthUser().Username;
BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category);
return true;
}
return false;
}
void SearchModel::SetLoadedSave(SaveInfo * save)
{
if(loadedSave != save && loadedSave)
delete loadedSave;
if(save)
{
loadedSave = new SaveInfo(*save);
}
else
{
loadedSave = NULL;
}
}
SaveInfo * SearchModel::GetLoadedSave(){
return loadedSave;
}
std::vector<SaveInfo*> SearchModel::GetSaveList()
{
return saveList;
}
std::vector<std::pair<ByteString, int> > SearchModel::GetTagList()
{
return tagList;
}
void SearchModel::Update()
{
if (searchSaves && searchSaves->CheckDone())
{
saveListLoaded = true;
lastError = "";
saveList = EndSearchSaves();
notifyPageChanged();
notifySaveListChanged();
}
if (getTags && getTags->CheckDone())
{
lastError = "";
tagList = EndGetTags();
notifyTagListChanged();
}
}
void SearchModel::AddObserver(SearchView * observer)
{
observers.push_back(observer);
observer->NotifySaveListChanged(this);
observer->NotifyPageChanged(this);
observer->NotifySortChanged(this);
observer->NotifyShowOwnChanged(this);
observer->NotifyTagListChanged(this);
}
void SearchModel::SelectSave(int saveID)
{
for (size_t i = 0; i < selected.size(); i++)
{
if (selected[i] == saveID)
{
return;
}
}
selected.push_back(saveID);
notifySelectedChanged();
}
void SearchModel::SelectAllSaves()
{
if (selected.size() == saveList.size())
{
for (auto &save : saveList)
{
DeselectSave(save->id);
}
}
else
{
for (auto &save : saveList)
{
SelectSave(save->id);
}
}
}
void SearchModel::DeselectSave(int saveID)
{
bool changed = false;
restart:
for (size_t i = 0; i < selected.size(); i++)
{
if (selected[i] == saveID)
{
selected.erase(selected.begin()+i);
changed = true;
goto restart; //Just ensure all cases are removed.
}
}
if(changed)
notifySelectedChanged();
}
void SearchModel::notifySaveListChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifySaveListChanged(this);
}
}
void SearchModel::notifyTagListChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifyTagListChanged(this);
}
}
void SearchModel::notifyPageChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifyPageChanged(this);
}
}
void SearchModel::notifySortChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifySortChanged(this);
}
}
void SearchModel::notifyShowOwnChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifyShowOwnChanged(this);
}
}
void SearchModel::notifyShowFavouriteChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifyShowOwnChanged(this);
}
}
void SearchModel::notifySelectedChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifySelectedChanged(this);
}
}
SearchModel::~SearchModel()
{
delete loadedSave;
}
int SearchModel::GetPageCount()
{
if (!showOwn && !showFavourite && currentSort == "best" && lastQuery == "")
return std::max(1, (int)(ceil(resultCount/20.0f))+1); //add one for front page (front page saves are repeated twice)
else
return std::max(1, (int)(ceil(resultCount/20.0f)));
}