[FIRE-35582] Add stores support to Primfeed

master
Beq 2025-06-07 00:49:05 +01:00
parent 594fd568f6
commit 7941a0b33d
7 changed files with 144 additions and 64 deletions

View File

@ -74,6 +74,7 @@ FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
mDescriptionTextBox(nullptr),
mLocationCheckbox(nullptr),
mRatingComboBox(nullptr),
mStoresComboBox(nullptr),
mPostButton(nullptr),
mBtnPreview(nullptr),
mBigPreviewFloater(nullptr)
@ -88,6 +89,16 @@ FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
LL_DEBUGS("primfeed") << "Info button clicked, opening " << url << LL_ENDL;
LLWeb::loadURLExternal(url);
});
FSPrimfeedAuth::sPrimfeedAuthPump->listen("FSPrimfeedPhotoPanel",
[this](const LLSD& data)
{
if ( data["responseType"].asString() == "primfeed_user_info" )
{
this->loadPrimfeedInfo(data);
return true;
}
return false;
});
}
FSPrimfeedPhotoPanel::~FSPrimfeedPhotoPanel()
@ -97,8 +108,6 @@ FSPrimfeedPhotoPanel::~FSPrimfeedPhotoPanel()
mPreviewHandle.get()->die();
}
FSPrimfeedAuth::sPrimfeedAuthPump->stopListening("FSPrimfeedAccountPanel");
gSavedSettings.setS32("FSLastSnapshotToPrimfeedResolution", getChild<LLComboBox>("resolution_combobox")->getCurrentIndex());
gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", getChild<LLSpinCtrl>("custom_snapshot_width")->getValue().asInteger());
gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", getChild<LLSpinCtrl>("custom_snapshot_height")->getValue().asInteger());
@ -121,6 +130,7 @@ bool FSPrimfeedPhotoPanel::postBuild()
mCommercialCheckbox = getChild<LLUICtrl>("primfeed_commercial_content");
mPublicGalleryCheckbox = getChild<LLUICtrl>("primfeed_add_to_public_gallery");
mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
mStoresComboBox = getChild<LLComboBox>("stores_combobox");
mPostButton = getChild<LLUICtrl>("post_photo_btn");
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
@ -192,6 +202,8 @@ void FSPrimfeedPhotoPanel::draw()
mCancelButton->setEnabled(can_post);
mDescriptionTextBox->setEnabled(can_post);
mRatingComboBox->setEnabled(can_post);
// If the stores combo box is present, enable it only if we have stores to select from
mStoresComboBox->setEnabled(can_post && ((LLComboBox*)mStoresComboBox)->getItemCount() > 1);
mResolutionComboBox->setEnabled(can_post);
mFilterComboBox->setEnabled(can_post);
mRefreshBtn->setEnabled(can_post);
@ -327,7 +339,7 @@ bool FSPrimfeedPhotoPanel::onPrimfeedConnectStateChange(const LLSD& /*data*/)
{
if (FSPrimfeedAuth::isAuthorized())
{
sendPhoto();
return true;
}
return false;
@ -355,7 +367,7 @@ void FSPrimfeedPhotoPanel::sendPhoto()
int content_rating = mRatingComboBox->getValue().asInteger();
bool post_to_public_gallery = mPublicGalleryCheckbox->getValue().asBoolean();
bool commercial_content = mCommercialCheckbox->getValue().asBoolean();
std::string store_id = mStoresComboBox->getValue().asString();
// Get the image
LLSnapshotLivePreview* previewp = getPreviewView();
@ -376,6 +388,11 @@ void FSPrimfeedPhotoPanel::sendPhoto()
params["location"] = slurl_string;
}
if (!store_id.empty())
{
// Add the store ID if we have one selected
params["store_id"] = store_id;
}
FSPrimfeedConnect::instance().uploadPhoto(params, previewp->getFormattedImage().get(),
[this](bool success, const std::string& url)
@ -529,6 +546,34 @@ void FSPrimfeedPhotoPanel::checkAspectRatio(S32 index)
}
}
void FSPrimfeedPhotoPanel::loadPrimfeedInfo(LLSD const& data)
{
if (!mStoresComboBox) return;
// Clear any existing entries
mStoresComboBox->clearRows();
mStoresComboBox->add(LLTrans::getString("Personal"), LLSD(""));
// Read the saved stores array
LLSD stores = data["stores"];
if (!stores.isArray() || stores.size() == 0)
{
// No stores - disable the combobox
mStoresComboBox->setEnabled(FALSE);
return;
}
mStoresComboBox->setEnabled(TRUE);
for (S32 i = 0; i < stores.size(); ++i)
{
LLSD const& store = stores[i];
std::string id = store["id"].asString();
std::string name = store["name"].asString();
mStoresComboBox->add(name, LLSD(id));
}
mStoresComboBox->setCurrentByIndex(0);
}
LLUICtrl* FSPrimfeedPhotoPanel::getRefreshBtn()
{
return mRefreshBtn;
@ -536,13 +581,10 @@ LLUICtrl* FSPrimfeedPhotoPanel::getRefreshBtn()
void FSPrimfeedPhotoPanel::onOpen(const LLSD& key)
{
if (!FSPrimfeedAuth::isAuthorized())
{
// Reauthorise if necessary.
FSPrimfeedAuth::initiateAuthRequest();
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
}
// Reauthorise if necessary.
FSPrimfeedAuth::initiateAuthRequest();
LLSD dummy;
onPrimfeedConnectStateChange(dummy);
}
void FSPrimfeedPhotoPanel::uploadCallback(bool success, const LLSD& response)
@ -621,6 +663,7 @@ bool FSPrimfeedPhotoPanel::checkImageSize(LLSnapshotLivePreview* previewp, S32&
return (w != width || h != height);
}
///////////////////////////
// FSPrimfeedAccountPanel///
///////////////////////////
@ -639,10 +682,24 @@ FSPrimfeedAccountPanel::FSPrimfeedAccountPanel() :
FSPrimfeedAuth::sPrimfeedAuthPump->listen("FSPrimfeedAccountPanel",
[this](const LLSD& data)
{
bool success = data["success"].asBoolean();
primfeedAuthResponse(success, data);
return true;
});
if ( data.has("responseType"))
{
auto response_type = data["responseType"].asString();
if(response_type == "primfeed_auth_response")
{
bool success = data["success"].asBoolean();
primfeedAuthResponse(success, data);
return true;
}
if(response_type == "primfeed_auth_reset")
{
bool success = data["success"].asBoolean();
primfeedAuthResponse(success, data);
return true;
}
}
return false;
});
setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
}
@ -782,6 +839,12 @@ void FSPrimfeedAccountPanel::onDisconnect()
onPrimfeedConnectStateChange(dummy);
}
FSPrimfeedAccountPanel::~FSPrimfeedAccountPanel()
{
// Disconnect the primfeed auth pump
FSPrimfeedAuth::sPrimfeedAuthPump->stopListening("FSPrimfeedAccountPanel");
}
////////////////////////
// FSFloaterPrimfeed/////
////////////////////////
@ -789,10 +852,10 @@ void FSPrimfeedAccountPanel::onDisconnect()
FSFloaterPrimfeed::FSFloaterPrimfeed(const LLSD& key) :
LLFloater(key),
mPrimfeedPhotoPanel(nullptr),
mPrimfeedAccountPanel(nullptr),
mStatusErrorText(nullptr),
mStatusLoadingText(nullptr),
mStatusLoadingIndicator(nullptr),
mPrimfeedAccountPanel(nullptr)
mStatusLoadingIndicator(nullptr)
{
mCommitCallbackRegistrar.add("SocialSharing.Cancel", [this](LLUICtrl*, const LLSD&) { onCancel(); });
}

View File

@ -34,6 +34,7 @@
class LLIconCtrl;
class LLCheckBoxCtrl;
class LLComboBox;
class LLSnapshotLivePreview;
class LLFloaterBigPreview;
@ -52,6 +53,7 @@ public:
bool postBuild() override;
S32 notify(const LLSD& info);
void draw() override;
void loadPrimfeedInfo(LLSD const& data);
LLSnapshotLivePreview* getPreviewView();
void onVisibilityChange(bool new_visibility);
@ -94,6 +96,7 @@ private:
LLUICtrl * mPostButton;
LLUICtrl * mCancelButton;
LLButton * mBtnPreview;
LLComboBox * mStoresComboBox;
LLFloaterBigPreview * mBigPreviewFloater;
};
@ -102,6 +105,7 @@ class FSPrimfeedAccountPanel : public LLPanel
{
public:
FSPrimfeedAccountPanel();
~FSPrimfeedAccountPanel();
bool postBuild() override;
void draw() override;

View File

@ -112,31 +112,25 @@ void FSPrimfeedAuth::initiateAuthRequest()
// It should be called when the user clicks the "Authenticate" button.
// Also triggered on opening the floater.
// The actual implementation is in the create() method.
if (!isAuthorized())
if (sPrimfeedAuth)
{
if (sPrimfeedAuth)
LLNotificationsUtil::add("PrimfeedAuthorizationAlreadyInProgress");
return;
}
// If no token stored, begin the login request; otherwise check user status.
sPrimfeedAuth = FSPrimfeedAuth::create(
[](bool success, const LLSD &response)
{
LLNotificationsUtil::add("PrimfeedAuthorizationAlreadyInProgress");
return;
LLSD event_data = response;
event_data["responseType"] = "primfeed_auth_response";
event_data["success"] = success;
sPrimfeedAuthPump->post(event_data);
// Now that auth is complete, clear the static pointer.
sPrimfeedAuth.reset();
}
// If no token stored, begin the login request; otherwise check user status.
sPrimfeedAuth = FSPrimfeedAuth::create(
[](bool success, const LLSD &response)
{
LLSD event_data = response;
event_data["success"] = success;
sPrimfeedAuthPump->post(event_data);
// Now that auth is complete, clear the static pointer.
sPrimfeedAuth.reset();
}
);
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
}
else
{
LLNotificationsUtil::add("PrimfeedAlreadyAuthorized");
}
);
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
}
void FSPrimfeedAuth::resetAuthStatus()
@ -147,7 +141,7 @@ void FSPrimfeedAuth::resetAuthStatus()
gSavedPerAccountSettings.setString("FSPrimfeedPlan", "");
gSavedPerAccountSettings.setString("FSPrimfeedUsername", "");
LLSD event_data;
event_data["status"] = "reset";
event_data["responseType"] = "primfeed_auth_reset";
event_data["success"] = "false";
sPrimfeedAuthPump->post(event_data);
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
@ -201,7 +195,8 @@ std::shared_ptr<FSPrimfeedAuth> FSPrimfeedAuth::create(authorized_callback_t cal
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
// If no token stored, begin the login request; otherwise check user status.
if (gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken").empty())
auth->mOauthToken = gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken");
if (auth->mOauthToken.empty())
{
auth->beginLoginRequest();
}
@ -432,22 +427,23 @@ void FSPrimfeedAuth::checkUserStatus()
}
void FSPrimfeedAuth::gotUserStatus(bool success, const LLSD &response)
void FSPrimfeedAuth::gotUserStatus(bool success, const LLSD &response) const
{
LL_INFOS("Primfeed") << "User status: " << response << "(" << success << ")" << LL_ENDL;
if (success && response.has("plan"))
if (success && response.has("plan") && response.has("username") && response.has("link"))
{
gSavedPerAccountSettings.setString("FSPrimfeedOAuthToken", mOauthToken);
gSavedPerAccountSettings.setString("FSPrimfeedPlan", response["plan"].asString());
gSavedPerAccountSettings.setString("FSPrimfeedProfileLink", response["link"].asString());
gSavedPerAccountSettings.setString("FSPrimfeedUsername", response["username"].asString());
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTED);
LLSD event_data = response;
event_data["responseType"] = "primfeed_user_info";
sPrimfeedAuthPump->post(event_data);
mCallback(true, response);
return;
}
else
{
LLNotificationsUtil::add("PrimfeedUserStatusFailed");
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
mCallback(false, response);
}
LLNotificationsUtil::add("PrimfeedUserStatusFailed");
FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
mCallback(false, response);
}

View File

@ -79,7 +79,7 @@ private:
// Callback when the validate response is received.
void gotValidateResponse(bool success, const LLSD &response);
// Callback when the user status response is received.
void gotUserStatus(bool success, const LLSD &response);
void gotUserStatus(bool success, const LLSD &response) const;
boost::signals2::connection mInstantMessageConnection;
boost::signals2::connection mChatMessageConnection;

View File

@ -89,6 +89,7 @@ void FSPrimfeedConnect::uploadPhotoCoro(const LLSD& params, LLImageFormatted* im
addPart("location", params["location"].asString());
}
LL_DEBUGS("primfeed") << "Adding image file header" << LL_ENDL;
body << dash << sep
<< "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << fmt << "\"" << sep
@ -122,12 +123,11 @@ void FSPrimfeedConnect::uploadPhotoCoro(const LLSD& params, LLImageFormatted* im
headers->append(HTTP_OUT_HEADER_USER_AGENT, FS_PF_USER_AGENT);
headers->append("Authorization", "Bearer " + token);
headers->append("pf-viewer-api-key", apiKey);
headers->append("Content-Type", "multipart/form-data; boundary=" + boundary);
LL_DEBUGS("primfeed") << "Dumping HTTP headers for POST:" << LL_ENDL;
for (auto it = headers->begin(); it != headers->end(); ++it)
if (params.has("store_id") && !params["store_id"].asString().empty())
{
LL_DEBUGS("primfeed") << it->first << ": " << it->second << LL_ENDL;
headers->append("kynno-selected-store", params["store_id"].asString());
}
headers->append("Content-Type", "multipart/form-data; boundary=" + boundary);
LL_DEBUGS("primfeed") << "Headers set" << LL_ENDL;
LL_DEBUGS("primfeed") << "Starting HTTP POST" << LL_ENDL;
@ -182,9 +182,3 @@ bool FSPrimfeedConnect::isTransactionOngoing() const
mConnectionState == PRIMFEED_DISCONNECTING);
}
void FSPrimfeedConnect::loadPrimfeedInfo()
{
LL_DEBUGS("primfeed") << "loadPrimfeedInfo() called" << LL_ENDL;
// Nothing to do here for Primfeed
setConnectionState(PRIMFEED_CONNECTED);
}

View File

@ -10,10 +10,10 @@
single_instance="true"
reuse_instance="true"
title="Share to Primfeed"
height="600"
height="630"
width="272">
<panel
height="590"
height="630"
width="272"
visible="true"
name="background"
@ -27,7 +27,7 @@
tab_height="21"
tab_position="top"
top="7"
height="570"
height="600"
follows="all"
halign="center">
<panel

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<panel
height="540"
height="590"
width="272"
follows="all"
layout="topleft"
@ -192,6 +192,29 @@
width="100">
<button.commit_callback function="SocialSharing.BigPreview" />
</button>
<text
follows="left|top"
layout="topleft"
font="SansSerif"
height="16"
left="10"
name="store_label"
top_pad="8"
type="string">
Post as:
</text>
<combo_box
control_name="FSPrimfeedSelectedStore"
follows="left|top"
layout="topleft"
name="stores_combobox"
tool_tip="If you have one or more easy blogger stores assigned on Primfeed they will appear here."
top_pad="2"
left="10"
height="21"
width="235">
<!-- Initially empty; items will be populated at runtime -->
</combo_box>
<text
length="1"
follows="top|left|right"