mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'CURA-8112_Inform_the_user_about_their_subscription_limits'
This commit is contained in:
commit
cc82ff97a7
6 changed files with 82 additions and 8 deletions
|
@ -20,7 +20,7 @@ Item
|
|||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
property alias createNewProjectButtonVisible: createNewProjectButton.visible
|
||||
property bool createNewProjectButtonVisible: true
|
||||
|
||||
anchors
|
||||
{
|
||||
|
@ -61,6 +61,7 @@ Item
|
|||
id: createNewProjectButton
|
||||
|
||||
text: "New Library project"
|
||||
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||
|
||||
onClicked:
|
||||
{
|
||||
|
@ -68,6 +69,20 @@ Item
|
|||
}
|
||||
busy: manager.creatingNewProjectStatus == DF.RetrievalStatus.InProgress
|
||||
}
|
||||
|
||||
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: upgradePlanButton
|
||||
|
||||
text: "Upgrade plan"
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||
tooltip: "You have reached the maximum number of projects allowed by your subscription. Please upgrade to the Professional subscription to create more projects."
|
||||
tooltipWidth: parent.width * 0.5
|
||||
|
||||
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-essentials/sign-up-cura?utm_source=cura&utm_medium=software&utm_campaign=lib-max")
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
|
|
|
@ -55,6 +55,7 @@ class DigitalFactoryApiClient:
|
|||
self._http = HttpRequestManager.getInstance()
|
||||
self._on_error = on_error
|
||||
self._file_uploader = None # type: Optional[DFFileUploader]
|
||||
self._library_max_private_projects: Optional[int] = None
|
||||
|
||||
self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager]
|
||||
|
||||
|
@ -69,6 +70,7 @@ class DigitalFactoryApiClient:
|
|||
callback(
|
||||
response.library_max_private_projects == -1 or # Note: -1 is unlimited
|
||||
response.library_max_private_projects > 0)
|
||||
self._library_max_private_projects = response.library_max_private_projects
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Response is not a feature budget, likely an error: {str(response)}")
|
||||
callback(False)
|
||||
|
@ -79,6 +81,41 @@ class DigitalFactoryApiClient:
|
|||
error_callback = callbackWrap,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def checkUserCanCreateNewLibraryProject(self, callback: Callable) -> None:
|
||||
"""
|
||||
Checks if the user is allowed to create new library projects.
|
||||
A user is allowed to create new library projects if the haven't reached their maximum allowed private projects.
|
||||
"""
|
||||
|
||||
def callbackWrap(response: Optional[Any] = None, *args, **kwargs) -> None:
|
||||
if response is not None:
|
||||
if isinstance(response, DigitalFactoryProjectResponse): # The user has only one private project
|
||||
callback(True)
|
||||
elif isinstance(response, list) and all(isinstance(r, DigitalFactoryProjectResponse) for r in response):
|
||||
callback(len(response) < cast(int, self._library_max_private_projects))
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Incorrect response type received when requesting private projects: {str(response)}")
|
||||
callback(False)
|
||||
else:
|
||||
Logger.warning(f"Digital Factory: Response is empty, likely an error: {str(response)}")
|
||||
callback(False)
|
||||
|
||||
if self._library_max_private_projects is not None and self._library_max_private_projects > 0:
|
||||
# The user has a limit in the number of private projects they can create. Check whether they have already
|
||||
# reached that limit.
|
||||
# Note: Set the pagination manager to None when doing this get request, or else the next/previous links
|
||||
# of the pagination will become corrupted
|
||||
url = f"{self.CURA_API_ROOT}/projects?shared=false&limit={self._library_max_private_projects}"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(callbackWrap, DigitalFactoryProjectResponse, callbackWrap, pagination_manager = None),
|
||||
error_callback = callbackWrap,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
else:
|
||||
# If the limit is -1, then the user is allowed unlimited projects. If its 0 then they are not allowed to
|
||||
# create any projects
|
||||
callback(self._library_max_private_projects == -1)
|
||||
|
||||
def getProject(self, library_project_id: str, on_finished: Callable[[DigitalFactoryProjectResponse], Any], failed: Callable) -> None:
|
||||
"""
|
||||
Retrieves a digital factory project by its library project id.
|
||||
|
|
|
@ -94,6 +94,9 @@ class DigitalFactoryController(QObject):
|
|||
"""Signal to inform about the state of user access."""
|
||||
userAccessStateChanged = pyqtSignal(bool)
|
||||
|
||||
"""Signal to inform whether the user is allowed to create more Library projects."""
|
||||
userCanCreateNewLibraryProjectChanged = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, application: CuraApplication) -> None:
|
||||
super().__init__(parent = None)
|
||||
|
||||
|
@ -143,6 +146,7 @@ class DigitalFactoryController(QObject):
|
|||
self._application.initializationFinished.connect(self._applicationInitializationFinished)
|
||||
|
||||
self._user_has_access = False
|
||||
self._user_account_can_create_new_project = False
|
||||
|
||||
def clear(self) -> None:
|
||||
self._project_model.clearProjects()
|
||||
|
@ -169,10 +173,8 @@ class DigitalFactoryController(QObject):
|
|||
|
||||
:return: True if the user account has Digital Library access, else False
|
||||
"""
|
||||
if self._account.userProfile:
|
||||
subscriptions = self._account.userProfile.get("subscriptions", [])
|
||||
if len(subscriptions) > 0:
|
||||
return True
|
||||
if self._user_has_access:
|
||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||
return self._user_has_access
|
||||
|
||||
def initialize(self, preselected_project_id: Optional[str] = None) -> None:
|
||||
|
@ -556,6 +558,7 @@ class DigitalFactoryController(QObject):
|
|||
self._project_model.clearProjects()
|
||||
self.setSelectedProjectIndex(-1)
|
||||
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
||||
self._has_preselected_project = new_has_preselected_project
|
||||
self.preselectedProjectChanged.emit()
|
||||
|
@ -564,6 +567,14 @@ class DigitalFactoryController(QObject):
|
|||
def hasPreselectedProject(self) -> bool:
|
||||
return self._has_preselected_project
|
||||
|
||||
def setCanCreateNewLibraryProject(self, can_create_new_library_project: bool) -> None:
|
||||
self._user_account_can_create_new_project = can_create_new_library_project
|
||||
self.userCanCreateNewLibraryProjectChanged.emit(self._user_account_can_create_new_project)
|
||||
|
||||
@pyqtProperty(bool, fset = setCanCreateNewLibraryProject, notify = userCanCreateNewLibraryProjectChanged)
|
||||
def userAccountCanCreateNewLibraryProject(self) -> bool:
|
||||
return self._user_account_can_create_new_project
|
||||
|
||||
@pyqtSlot(str, "QStringList")
|
||||
def saveFileToSelectedProject(self, filename: str, formats: List[str]) -> None:
|
||||
"""
|
||||
|
|
|
@ -16,9 +16,9 @@ class DigitalFactoryFeatureBudgetResponse(BaseModel):
|
|||
library_can_use_status: Optional[bool] = False,
|
||||
library_can_use_tags: Optional[bool] = False,
|
||||
library_can_use_technical_requirements: Optional[bool] = False,
|
||||
library_max_organization_shared_projects: Optional[int] = False, # -1 means unlimited
|
||||
library_max_private_projects: Optional[int] = False, # -1 means unlimited
|
||||
library_max_team_shared_projects: Optional[int] = False, # -1 means unlimited
|
||||
library_max_organization_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||
library_max_private_projects: Optional[int] = None, # -1 means unlimited
|
||||
library_max_team_shared_projects: Optional[int] = None, # -1 means unlimited
|
||||
**kwargs) -> None:
|
||||
|
||||
self.library_can_use_business_value = library_can_use_business_value
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue