1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Projucer: Remove user login and license check code

This commit is contained in:
Oliver James 2024-06-04 09:58:09 +01:00
parent fcaaf38c58
commit 7306fe1789
31 changed files with 108 additions and 2007 deletions

View file

@ -1,229 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include "jucer_LicenseState.h"
#include "jucer_LicenseQueryThread.h"
//==============================================================================
class LicenseController final : private Timer
{
public:
LicenseController()
{
checkLicense();
}
//==============================================================================
static LicenseState getAGPLState()
{
return { LicenseState::Type::agplv3, projucerMajorVersion, {}, {} };
}
LicenseState getCurrentState() const noexcept
{
return state;
}
void setState (const LicenseState& newState)
{
if (state != newState)
{
state = newState;
licenseStateToSettings (state, getGlobalProperties());
stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); });
}
}
void resetState()
{
setState ({});
}
void signIn (const String& email, const String& password,
std::function<void (const String&)> completionCallback)
{
licenseQueryThread.doSignIn (email, password,
[this, completionCallback] (LicenseQueryThread::ErrorMessageAndType error,
LicenseState newState)
{
completionCallback (error.first);
setState (newState);
});
}
void cancelSignIn()
{
licenseQueryThread.cancelRunningJobs();
}
//==============================================================================
struct LicenseStateListener
{
virtual ~LicenseStateListener() = default;
virtual void licenseStateChanged() = 0;
};
void addListener (LicenseStateListener* listenerToAdd)
{
stateListeners.add (listenerToAdd);
}
void removeListener (LicenseStateListener* listenerToRemove)
{
stateListeners.remove (listenerToRemove);
}
private:
//==============================================================================
static const char* getLicenseStateValue (LicenseState::Type type)
{
switch (type)
{
case LicenseState::Type::agplv3: return "AGPLv3";
case LicenseState::Type::personal: return "personal";
case LicenseState::Type::educational: return "edu";
case LicenseState::Type::indie: return "indie";
case LicenseState::Type::pro: return "pro";
case LicenseState::Type::none:
default: break;
}
return nullptr;
}
static LicenseState::Type getLicenseTypeFromValue (const String& d)
{
if (d == getLicenseStateValue (LicenseState::Type::agplv3)) return LicenseState::Type::agplv3;
if (d == getLicenseStateValue (LicenseState::Type::personal)) return LicenseState::Type::personal;
if (d == getLicenseStateValue (LicenseState::Type::educational)) return LicenseState::Type::educational;
if (d == getLicenseStateValue (LicenseState::Type::indie)) return LicenseState::Type::indie;
if (d == getLicenseStateValue (LicenseState::Type::pro)) return LicenseState::Type::pro;
return LicenseState::Type::none;
}
static LicenseState licenseStateFromSettings (PropertiesFile& props)
{
if (auto licenseXml = props.getXmlValue ("license"))
{
// this is here for backwards compatibility with old-style settings files using XML text elements
if (licenseXml->getChildElementAllSubText ("type", {}).isNotEmpty())
{
auto stateFromOldSettings = [&licenseXml]() -> LicenseState
{
return { getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {})),
licenseXml->getChildElementAllSubText ("version", "-1").getIntValue(),
licenseXml->getChildElementAllSubText ("username", {}),
licenseXml->getChildElementAllSubText ("authToken", {}) };
}();
licenseStateToSettings (stateFromOldSettings, props);
return stateFromOldSettings;
}
return { getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {})),
licenseXml->getIntAttribute ("version", -1),
licenseXml->getStringAttribute ("username", {}),
licenseXml->getStringAttribute ("authToken", {}) };
}
return {};
}
static void licenseStateToSettings (const LicenseState& state, PropertiesFile& props)
{
props.removeValue ("license");
if (state.isSignedIn())
{
XmlElement licenseXml ("license");
if (auto* typeString = getLicenseStateValue (state.type))
licenseXml.setAttribute ("type", typeString);
licenseXml.setAttribute ("version", state.version);
licenseXml.setAttribute ("username", state.username);
licenseXml.setAttribute ("authToken", state.authToken);
props.setValue ("license", &licenseXml);
}
props.saveIfNeeded();
}
//==============================================================================
void checkLicense()
{
if (state.authToken.isNotEmpty() && ! state.isAGPL())
{
auto completionCallback = [this] (LicenseQueryThread::ErrorMessageAndType error,
LicenseState updatedState)
{
if (error == LicenseQueryThread::ErrorMessageAndType())
{
setState (updatedState);
}
else if ((error.second == LicenseQueryThread::ErrorType::busy
|| error.second == LicenseQueryThread::ErrorType::cancelled
|| error.second == LicenseQueryThread::ErrorType::connectionError)
&& ! hasRetriedLicenseCheck)
{
hasRetriedLicenseCheck = true;
startTimer (10000);
}
};
licenseQueryThread.checkLicenseValidity (state, std::move (completionCallback));
}
}
void timerCallback() override
{
stopTimer();
checkLicense();
}
//==============================================================================
LicenseState state = licenseStateFromSettings (getGlobalProperties());
ListenerList<LicenseStateListener> stateListeners;
LicenseQueryThread licenseQueryThread;
bool hasRetriedLicenseCheck = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController)
};

View file

@ -1,380 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
namespace LicenseHelpers
{
inline LicenseState::Type licenseTypeForString (const String& licenseString)
{
if (licenseString == "juce-pro") return LicenseState::Type::pro;
if (licenseString == "juce-indie") return LicenseState::Type::indie;
if (licenseString == "juce-edu") return LicenseState::Type::educational;
if (licenseString == "juce-personal") return LicenseState::Type::personal;
jassertfalse; // unknown type
return LicenseState::Type::none;
}
using LicenseVersionAndType = std::pair<int, LicenseState::Type>;
inline LicenseVersionAndType findBestLicense (std::vector<LicenseVersionAndType>&& licenses)
{
if (licenses.size() == 1)
return licenses[0];
auto getValueForLicenceType = [] (LicenseState::Type type)
{
switch (type)
{
case LicenseState::Type::pro: return 4;
case LicenseState::Type::indie: return 3;
case LicenseState::Type::educational: return 2;
case LicenseState::Type::personal: return 1;
case LicenseState::Type::agplv3:
case LicenseState::Type::none:
default: return -1;
}
};
std::sort (licenses.begin(), licenses.end(),
[getValueForLicenceType] (const LicenseVersionAndType& l1, const LicenseVersionAndType& l2)
{
if (l1.first > l2.first)
return true;
if (l1.first == l2.first)
return getValueForLicenceType (l1.second) > getValueForLicenceType (l2.second);
return false;
});
auto findFirstLicense = [&licenses] (bool isPaid)
{
auto iter = std::find_if (licenses.begin(), licenses.end(),
[isPaid] (const LicenseVersionAndType& l)
{
auto proOrIndie = (l.second == LicenseState::Type::pro || l.second == LicenseState::Type::indie);
return isPaid ? proOrIndie : ! proOrIndie;
});
return iter != licenses.end() ? *iter
: LicenseVersionAndType();
};
auto newestPaid = findFirstLicense (true);
auto newestFree = findFirstLicense (false);
if (newestPaid.first >= projucerMajorVersion || newestPaid.first >= newestFree.first)
return newestPaid;
return newestFree;
}
}
//==============================================================================
class LicenseQueryThread
{
public:
enum class ErrorType
{
busy,
cancelled,
connectionError,
webResponseError
};
using ErrorMessageAndType = std::pair<String, ErrorType>;
using LicenseQueryCallback = std::function<void (ErrorMessageAndType, LicenseState)>;
//==============================================================================
LicenseQueryThread() = default;
void checkLicenseValidity (const LicenseState& state, LicenseQueryCallback completionCallback)
{
if (jobPool.getNumJobs() > 0)
{
completionCallback ({ {}, ErrorType::busy }, {});
return;
}
jobPool.addJob ([this, state, completionCallback]
{
auto updatedState = state;
auto result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), updatedState);
WeakReference<LicenseQueryThread> weakThis (this);
MessageManager::callAsync ([weakThis, result, updatedState, completionCallback]
{
if (weakThis != nullptr)
completionCallback (result, updatedState);
});
});
}
void doSignIn (const String& email, const String& password, LicenseQueryCallback completionCallback)
{
cancelRunningJobs();
jobPool.addJob ([this, email, password, completionCallback]
{
LicenseState state;
auto result = runTask (std::make_unique<UserLogin> (email, password), state);
if (result == ErrorMessageAndType())
result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), state);
if (result != ErrorMessageAndType())
state = {};
WeakReference<LicenseQueryThread> weakThis (this);
MessageManager::callAsync ([weakThis, result, state, completionCallback]
{
if (weakThis != nullptr)
completionCallback (result, state);
});
});
}
void cancelRunningJobs()
{
jobPool.removeAllJobs (true, 500);
}
private:
//==============================================================================
struct AccountEnquiryBase
{
virtual ~AccountEnquiryBase() = default;
virtual bool isPOSTLikeRequest() const = 0;
virtual String getEndpointURLSuffix() const = 0;
virtual StringPairArray getParameterNamesAndValues() const = 0;
virtual String getExtraHeaders() const = 0;
virtual int getSuccessCode() const = 0;
virtual String errorCodeToString (int) const = 0;
virtual bool parseServerResponse (const String&, LicenseState&) = 0;
};
struct UserLogin final : public AccountEnquiryBase
{
UserLogin (const String& e, const String& p)
: userEmail (e), userPassword (p)
{
}
bool isPOSTLikeRequest() const override { return true; }
String getEndpointURLSuffix() const override { return "/authenticate/projucer"; }
int getSuccessCode() const override { return 200; }
StringPairArray getParameterNamesAndValues() const override
{
StringPairArray namesAndValues;
namesAndValues.set ("email", userEmail);
namesAndValues.set ("password", userPassword);
return namesAndValues;
}
String getExtraHeaders() const override
{
return "Content-Type: application/json";
}
String errorCodeToString (int errorCode) const override
{
switch (errorCode)
{
case 400: return "Please enter your email and password to sign in.";
case 401: return "Your email and password are incorrect.";
case 451: return "Access denied.";
default: return "Something went wrong, please try again.";
}
}
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
{
auto json = JSON::parse (serverResponse);
licenseState.authToken = json.getProperty ("token", {}).toString();
licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString();
return (licenseState.authToken.isNotEmpty() && licenseState.username.isNotEmpty());
}
String userEmail, userPassword;
};
struct UserLicenseQuery final : public AccountEnquiryBase
{
UserLicenseQuery (const String& authToken)
: userAuthToken (authToken)
{
}
bool isPOSTLikeRequest() const override { return false; }
String getEndpointURLSuffix() const override { return "/user/licences/projucer"; }
int getSuccessCode() const override { return 200; }
StringPairArray getParameterNamesAndValues() const override
{
return {};
}
String getExtraHeaders() const override
{
return "x-access-token: " + userAuthToken;
}
String errorCodeToString (int errorCode) const override
{
switch (errorCode)
{
case 401: return "User not found or could not be verified.";
default: return "User licenses info fetch failed (unknown error).";
}
}
bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override
{
auto json = JSON::parse (serverResponse);
if (auto* licensesJson = json.getArray())
{
std::vector<LicenseHelpers::LicenseVersionAndType> licenses;
for (auto& license : *licensesJson)
{
auto version = license.getProperty ("product_version", {}).toString().trim();
auto type = license.getProperty ("licence_type", {}).toString();
auto status = license.getProperty ("status", {}).toString();
if (status == "active" && type.isNotEmpty() && version.isNotEmpty())
licenses.push_back ({ version.getIntValue(), LicenseHelpers::licenseTypeForString (type) });
}
if (! licenses.empty())
{
auto bestLicense = LicenseHelpers::findBestLicense (std::move (licenses));
licenseState.version = bestLicense.first;
licenseState.type = bestLicense.second;
}
return true;
}
return false;
}
String userAuthToken;
};
//==============================================================================
static String postDataStringAsJSON (const StringPairArray& parameters)
{
DynamicObject::Ptr d (new DynamicObject());
for (auto& key : parameters.getAllKeys())
d->setProperty (key, parameters[key]);
return JSON::toString (var (d.get()));
}
static ErrorMessageAndType runTask (std::unique_ptr<AccountEnquiryBase> accountEnquiryTask, LicenseState& state)
{
const ErrorMessageAndType cancelledError ("Cancelled.", ErrorType::cancelled);
const String endpointURL ("https://api.juce.com/api/v1");
URL url (endpointURL + accountEnquiryTask->getEndpointURLSuffix());
auto isPOST = accountEnquiryTask->isPOSTLikeRequest();
if (isPOST)
url = url.withPOSTData (postDataStringAsJSON (accountEnquiryTask->getParameterNamesAndValues()));
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
return cancelledError;
int statusCode = 0;
auto urlStream = url.createInputStream (URL::InputStreamOptions (isPOST ? URL::ParameterHandling::inPostData
: URL::ParameterHandling::inAddress)
.withExtraHeaders (accountEnquiryTask->getExtraHeaders())
.withConnectionTimeoutMs (5000)
.withStatusCode (&statusCode));
if (urlStream == nullptr)
return { "Failed to connect to the web server.", ErrorType::connectionError };
if (statusCode != accountEnquiryTask->getSuccessCode())
return { accountEnquiryTask->errorCodeToString (statusCode), ErrorType::webResponseError };
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
return cancelledError;
String response;
for (;;)
{
char buffer [8192] = "";
auto num = urlStream->read (buffer, sizeof (buffer));
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
return cancelledError;
if (num <= 0)
break;
response += buffer;
}
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
return cancelledError;
if (! accountEnquiryTask->parseServerResponse (response, state))
return { "Failed to parse server response.", ErrorType::webResponseError };
return {};
}
//==============================================================================
ThreadPool jobPool { ThreadPoolOptions{}.withNumberOfThreads (1) };
//==============================================================================
JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseQueryThread)
};

View file

@ -1,96 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
struct LicenseState
{
enum class Type
{
none,
agplv3,
personal,
educational,
indie,
pro
};
LicenseState() = default;
LicenseState (Type t, int v, String user, String token)
: type (t), version (v), username (user), authToken (token)
{
}
bool operator== (const LicenseState& other) const noexcept
{
return type == other.type
&& version == other.version
&& username == other.username
&& authToken == other.authToken;
}
bool operator != (const LicenseState& other) const noexcept
{
return ! operator== (other);
}
bool isSignedIn() const noexcept { return isAGPL() || (version > 0 && username.isNotEmpty()); }
bool isOldLicense() const noexcept { return isSignedIn() && version < projucerMajorVersion; }
bool isAGPL() const noexcept { return type == Type::agplv3; }
bool isPersonalOrNone() const noexcept { return type == Type::none || type == Type::personal; }
String getLicenseTypeString() const
{
switch (type)
{
case Type::none: return "No license";
case Type::agplv3: return "AGPLv3";
case Type::personal: return "Personal";
case Type::educational: return "Educational";
case Type::indie: return "Indie";
case Type::pro: return "Pro";
default: break;
};
jassertfalse;
return {};
}
Type type = Type::none;
int version = -1;
String username, authToken;
};

View file

@ -1,297 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include "../../Project/UI/jucer_UserAvatarComponent.h"
//==============================================================================
class LoginFormComponent final : public Component
{
public:
LoginFormComponent (MainWindow& window)
: mainWindow (window)
{
setTitle ("Login");
setFocusContainerType (FocusContainerType::focusContainer);
addAndMakeVisible (emailBox);
emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f));
emailBox.setJustification (Justification::centredLeft);
emailBox.onReturnKey = [this] { submitDetails(); };
emailBox.setTitle ("Email");
addAndMakeVisible (passwordBox);
passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f));
passwordBox.setPasswordCharacter ((juce_wchar) 0x2022);
passwordBox.setJustification (Justification::centredLeft);
passwordBox.onReturnKey = [this] { submitDetails(); };
passwordBox.setTitle ("Password");
addAndMakeVisible (logInButton);
logInButton.onClick = [this] { submitDetails(); };
addAndMakeVisible (enableAGPLButton);
enableAGPLButton.onClick = [this]
{
ProjucerApplication::getApp().getLicenseController().setState (LicenseController::getAGPLState());
mainWindow.hideLoginFormOverlay();
};
addAndMakeVisible (userAvatar);
addAndMakeVisible (createAccountLabel);
createAccountLabel.setFont (FontOptions (14.0f, Font::underlined));
createAccountLabel.addMouseListener (this, false);
createAccountLabel.setMouseCursor (MouseCursor::PointingHandCursor);
addAndMakeVisible (errorMessageLabel);
errorMessageLabel.setMinimumHorizontalScale (1.0f);
errorMessageLabel.setFont (FontOptions { 12.0f });
errorMessageLabel.setColour (Label::textColourId, Colours::red);
errorMessageLabel.setVisible (false);
dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
addAndMakeVisible (dismissButton);
dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); };
dismissButton.setTitle ("Dismiss");
setWantsKeyboardFocus (true);
setOpaque (true);
updateLookAndFeel();
setSize (300, 350);
}
~LoginFormComponent() override
{
ProjucerApplication::getApp().getLicenseController().cancelSignIn();
}
void resized() override
{
auto bounds = getLocalBounds().reduced (20);
auto spacing = bounds.getHeight() / 20;
userAvatar.setBounds (bounds.removeFromTop (iconHeight).reduced ((bounds.getWidth() / 2) - (iconHeight / 2), 0));
errorMessageLabel.setBounds (bounds.removeFromTop (spacing));
bounds.removeFromTop (spacing / 2);
auto textEditorHeight = bounds.getHeight() / 5;
emailBox.setBounds (bounds.removeFromTop (textEditorHeight));
bounds.removeFromTop (spacing);
passwordBox.setBounds (bounds.removeFromTop (textEditorHeight));
bounds.removeFromTop (spacing * 2);
emailBox.setFont (FontOptions ((float) textEditorHeight / 2.5f));
passwordBox.setFont (FontOptions ((float) textEditorHeight / 2.5f));
logInButton.setBounds (bounds.removeFromTop (textEditorHeight));
auto slice = bounds.removeFromTop (textEditorHeight);
createAccountLabel.setBounds (slice.removeFromLeft (createAccountLabel.getFont().getStringWidth (createAccountLabel.getText()) + 5));
slice.removeFromLeft (15);
enableAGPLButton.setBounds (slice.reduced (0, 5));
dismissButton.setBounds (getLocalBounds().reduced (10).removeFromTop (20).removeFromRight (20));
}
void paint (Graphics& g) override
{
g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
}
void mouseUp (const MouseEvent& event) override
{
if (event.eventComponent == &createAccountLabel)
URL ("https://juce.com/verification/register").launchInDefaultBrowser();
}
void updateLookAndFeel()
{
enableAGPLButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
}
void lookAndFeelChanged() override
{
updateLookAndFeel();
}
private:
class ProgressButton final : public TextButton,
private Timer
{
public:
ProgressButton (const String& buttonName)
: TextButton (buttonName), text (buttonName)
{
}
void setBusy (bool shouldBeBusy)
{
isInProgress = shouldBeBusy;
if (isInProgress)
{
setEnabled (false);
setButtonText ({});
startTimerHz (30);
}
else
{
setEnabled (true);
setButtonText (text);
stopTimer();
}
}
void paint (Graphics& g) override
{
TextButton::paint (g);
if (isInProgress)
{
auto size = getHeight() - 10;
auto halfSize = size / 2;
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white,
(getWidth() / 2) - halfSize, (getHeight() / 2) - halfSize,
size, size);
}
}
private:
void timerCallback() override
{
repaint();
}
String text;
bool isInProgress = false;
};
//==============================================================================
void updateLoginButtonStates (bool isLoggingIn)
{
logInButton.setBusy (isLoggingIn);
emailBox.setEnabled (! isLoggingIn);
passwordBox.setEnabled (! isLoggingIn);
}
void submitDetails()
{
auto loginFormError = checkLoginFormsAreValid();
if (loginFormError.isNotEmpty())
{
showErrorMessage (loginFormError);
return;
}
updateLoginButtonStates (true);
auto completionCallback = [weakThis = SafePointer<LoginFormComponent> { this }] (const String& errorMessage)
{
if (weakThis == nullptr)
return;
weakThis->updateLoginButtonStates (false);
if (errorMessage.isNotEmpty())
{
weakThis->showErrorMessage (errorMessage);
}
else
{
weakThis->hideErrorMessage();
weakThis->mainWindow.hideLoginFormOverlay();
ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
}
};
ProjucerApplication::getApp().getLicenseController().signIn (emailBox.getText(), passwordBox.getText(),
std::move (completionCallback));
}
String checkLoginFormsAreValid() const
{
auto email = emailBox.getText();
if (email.isEmpty() || email.indexOfChar ('@') < 0)
return "Please enter a valid email.";
auto password = passwordBox.getText();
if (password.isEmpty() || password.length() < 8)
return "Please enter a valid password.";
return {};
}
void showErrorMessage (const String& errorMessage)
{
errorMessageLabel.setText (errorMessage, dontSendNotification);
errorMessageLabel.setVisible (true);
}
void hideErrorMessage()
{
errorMessageLabel.setText ({}, dontSendNotification);
errorMessageLabel.setVisible (false);
}
//==============================================================================
static constexpr int iconHeight = 50;
MainWindow& mainWindow;
TextEditor emailBox, passwordBox;
ProgressButton logInButton { "Sign In" };
TextButton enableAGPLButton { "Enable AGPLv3 Mode" };
ShapeButton dismissButton { {},
findColour (treeIconColourId),
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
UserAvatarComponent userAvatar { false };
Label createAccountLabel { {}, "Create an account" },
errorMessageLabel { {}, {} };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent)
};

View file

@ -161,7 +161,6 @@ void ProjucerApplication::handleAsyncUpdate()
void ProjucerApplication::doBasicApplicationSetup()
{
licenseController = std::make_unique<LicenseController>();
LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
initCommandManager();
icons = std::make_unique<Icons>();
@ -373,8 +372,6 @@ PopupMenu ProjucerApplication::createFileMenu()
menu.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::openInIDE);
menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE);
menu.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::loginLogout);
#if ! JUCE_MAC
menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow);
@ -924,8 +921,7 @@ void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
CommandIDs::showForum,
CommandIDs::showAPIModules,
CommandIDs::showAPIClasses,
CommandIDs::showTutorials,
CommandIDs::loginLogout };
CommandIDs::showTutorials };
commands.addArray (ids, numElementsInArray (ids));
}
@ -1031,19 +1027,6 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman
result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0);
break;
case CommandIDs::loginLogout:
{
auto licenseState = licenseController->getCurrentState();
if (licenseState.isAGPL())
result.setInfo ("Disable AGPLv3 mode", "Disables AGPLv3 mode", CommandCategories::general, 0);
else
result.setInfo (licenseState.isSignedIn() ? String ("Sign out ") + licenseState.username + "..." : String ("Sign in..."),
"Sign out of your JUCE account",
CommandCategories::general, 0);
break;
}
default:
JUCEApplication::getCommandInfo (commandID, result);
break;
@ -1074,7 +1057,6 @@ bool ProjucerApplication::perform (const InvocationInfo& info)
case CommandIDs::showAPIModules: launchModulesBrowser(); break;
case CommandIDs::showAPIClasses: launchClassesBrowser(); break;
case CommandIDs::showTutorials: launchTutorialsBrowser(); break;
case CommandIDs::loginLogout: doLoginOrLogout(); break;
default: return JUCEApplication::perform (info);
}
@ -1307,26 +1289,6 @@ void ProjucerApplication::launchTutorialsBrowser()
tutorialsLink.launchInDefaultBrowser();
}
void ProjucerApplication::doLoginOrLogout()
{
if (licenseController->getCurrentState().isSignedIn())
{
licenseController->resetState();
}
else
{
if (auto* window = mainWindowList.getMainWindowWithLoginFormOpen())
{
window->toFront (true);
}
else
{
mainWindowList.createWindowIfNoneAreOpen();
mainWindowList.getFrontmostWindow()->showLoginFormOverlay();
}
}
}
//==============================================================================
struct FileWithTime
{

View file

@ -34,7 +34,6 @@
#pragma once
#include "UserAccount/jucer_LicenseController.h"
#include "jucer_MainWindow.h"
#include "../Project/Modules/jucer_Modules.h"
#include "jucer_AutoUpdater.h"
@ -87,8 +86,6 @@ public:
AvailableModulesList& getJUCEPathModulesList() { return jucePathModulesList; }
AvailableModulesList& getUserPathsModulesList() { return userPathsModulesList; }
LicenseController& getLicenseController() { return *licenseController; }
bool isAutomaticVersionCheckingEnabled() const;
void setAutomaticVersionCheckingEnabled (bool shouldBeEnabled);
@ -169,9 +166,6 @@ private:
void launchClassesBrowser();
void launchTutorialsBrowser();
void doLoginOrLogout();
void showLoginForm();
void enableOrDisableGUIEditor();
//==============================================================================
@ -208,8 +202,6 @@ private:
#endif
//==============================================================================
std::unique_ptr<LicenseController> licenseController;
std::unique_ptr<TooltipWindow> tooltipWindow;
AvailableModulesList jucePathModulesList, userPathsModulesList;

View file

@ -95,8 +95,6 @@ namespace CommandIDs
zoomNormal = 0x300073,
spaceBarDrag = 0x300074,
loginLogout = 0x300090,
showForum = 0x300100,
showAPIModules = 0x300101,
showAPIClasses = 0x300102,

View file

@ -38,7 +38,6 @@
#include "StartPage/jucer_StartPageComponent.h"
#include "../Utility/UI/jucer_JucerTreeViewBase.h"
#include "../ProjectSaving/jucer_ProjectSaver.h"
#include "UserAccount/jucer_LoginFormComponent.h"
#include "../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
@ -78,11 +77,6 @@ public:
g.drawImage (componentImage, getLocalBounds().toFloat());
}
void inputAttemptWhenModal() override
{
mainWindow.hideLoginFormOverlay();
}
private:
void componentPeerChanged() override {}
@ -96,12 +90,6 @@ private:
void handleAsyncUpdate() override { resized(); }
void mouseUp (const MouseEvent& event) override
{
if (event.eventComponent == this)
mainWindow.hideLoginFormOverlay();
}
void lookAndFeelChanged() override
{
refreshBackgroundImage();
@ -624,18 +612,6 @@ void MainWindow::showStartPage()
getContentComponent()->grabKeyboardFocus();
}
void MainWindow::showLoginFormOverlay()
{
blurOverlayComponent = std::make_unique<BlurOverlayWithComponent> (*this, std::make_unique<LoginFormComponent> (*this));
loginFormOpen = true;
}
void MainWindow::hideLoginFormOverlay()
{
blurOverlayComponent.reset();
loginFormOpen = false;
}
//==============================================================================
ApplicationCommandTarget* MainWindow::getNextCommandTarget()
{
@ -954,15 +930,6 @@ MainWindow* MainWindowList::getMainWindowForFile (const File& file)
return nullptr;
}
MainWindow* MainWindowList::getMainWindowWithLoginFormOpen()
{
for (auto* window : windows)
if (window->isShowingLoginForm())
return window;
return nullptr;
}
void MainWindowList::checkWindowBounds (MainWindow& windowToCheck)
{
auto avoidSuperimposedWindows = [&]

View file

@ -76,10 +76,6 @@ public:
void showStartPage();
void showLoginFormOverlay();
void hideLoginFormOverlay();
bool isShowingLoginForm() const noexcept { return loginFormOpen; }
bool isInterestedInFileDrag (const StringArray& files) override;
void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override;
@ -111,7 +107,6 @@ private:
Value projectNameValue;
std::unique_ptr<Component> blurOverlayComponent;
bool loginFormOpen = false;
ScopedMessageBox messageBox;
@ -138,7 +133,6 @@ public:
MainWindow* getFrontmostWindow (bool createIfNotFound = true);
MainWindow* getOrCreateEmptyWindow();
MainWindow* getMainWindowForFile (const File&);
MainWindow* getMainWindowWithLoginFormOpen();
Project* getFrontmostProject();