diff --git a/app/qml/CMakeLists.txt b/app/qml/CMakeLists.txt
index 53c1dc493..b795a448e 100644
--- a/app/qml/CMakeLists.txt
+++ b/app/qml/CMakeLists.txt
@@ -78,6 +78,8 @@ set(MM_QML
onboarding/MMSignUp.qml
onboarding/MMWhichIndustry.qml
onboarding/MMOnboardingController.qml
+ onboarding/MMCreateWorkspaceController.qml
+ onboarding/MMAcceptInvitationController.qml
dialogs/MigrateToMerginDialog.qml
dialogs/MissingAuthDialog.qml
dialogs/NoPermissionsDialog.qml
diff --git a/app/qml/ProjectPanel.qml b/app/qml/ProjectPanel.qml
index ce0b67dc7..374fc43eb 100644
--- a/app/qml/ProjectPanel.qml
+++ b/app/qml/ProjectPanel.qml
@@ -64,9 +64,9 @@ Item {
}
}
- function openLoginPage()
+ function showLogin()
{
- onboardingController.openLoginPage()
+ onboardingController.start()
}
function openChangesPanel()
@@ -99,7 +99,7 @@ Item {
return false;
}
// do not show the banner in case of accepting invitation or creating a workspace
- if (stackView.currentItem && (stackView.currentItem.objectName === "registrationFinishPanel" || stackView.currentItem.objectName === "createWorkspacePanel")) {
+ if (onboardingController.inProgress) {
return false;
}
return !__merginApi.userInfo.hasWorkspaces
@@ -112,7 +112,7 @@ Item {
}
onCreateWorkspaceRequested: {
- stackView.push(createWorkspaceComponent)
+ createWorkspaceController.createNewWorkspace()
}
}
@@ -253,7 +253,7 @@ Item {
}
}
else {
- root.openLoginPage()
+ root.showLogin()
}
}
}
@@ -496,6 +496,20 @@ Item {
stackView: stackView
}
+ MMCreateWorkspaceController {
+ // TODO move to main.qml
+ id: createWorkspaceController
+ enabled: root.visible
+ stackView: stackView
+ }
+
+ MMAcceptInvitationController {
+ // TODO move to main.qml
+ id: acceptInvitationController
+ // TODO enabled add controller.showInvitationsList
+ enabled: root.visible && __merginApi.apiSupportsWorkspaces
+ stackView: stackView
+ }
Component {
id: registrationFinishComponent
@@ -605,7 +619,7 @@ Item {
function onAuthRequested() {
stackView.pending = false
- root.openLoginPage()
+ root.showLogin()
}
function onAuthChanged() {
@@ -622,7 +636,7 @@ Item {
}
else {
// log out - reenable openInvitationsListener
- onboardingController.showInvitationList()
+ acceptInvitationController.showInvitationList = true
}
}
diff --git a/app/qml/main.qml b/app/qml/main.qml
index 591ada646..62b768f19 100644
--- a/app/qml/main.qml
+++ b/app/qml/main.qml
@@ -200,7 +200,7 @@ ApplicationWindow {
onSignInRequested: {
stateManager.state = "projects"
- projectPanel.openLoginPage()
+ projectPanel.showLogin()
}
onLocalChangesPanelRequested: {
diff --git a/app/qml/onboarding/MMAcceptInvitation.qml b/app/qml/onboarding/MMAcceptInvitation.qml
index deabc63b5..f7f42c04b 100644
--- a/app/qml/onboarding/MMAcceptInvitation.qml
+++ b/app/qml/onboarding/MMAcceptInvitation.qml
@@ -18,10 +18,14 @@ Page {
required property string user
required property string workspace
+ required property string workspaceUuid
- signal backClicked
- signal continueClicked
+ property bool haveBack: true
+ property bool showCreate: true
+
+ signal joinWorkspaceClicked(string workspaceUuid)
signal createWorkspaceClicked
+ signal backClicked
readonly property real hPadding: width < __style.maxPageWidth
? 20 * __dp
@@ -31,6 +35,17 @@ Page {
color: __style.lightGreenColor
}
+ MMHeader {
+ id: header
+
+ x: mainColumn.leftPadding
+ y: mainColumn.topPadding
+ width: parent.width - 2 * root.hPadding
+ visible: haveBack
+
+ onBackClicked: root.backClicked()
+ }
+
ScrollView {
width: parent.width
height: parent.height
@@ -105,17 +120,19 @@ Page {
width: parent.width - 2 * root.hPadding
text: qsTr("Join workspace")
- onClicked: root.continueClicked()
+ onClicked: root.joinWorkspaceClicked(root.workspaceUuid)
}
MMHlineText {
width: parent.width - 2 * root.hPadding
title: qsTr("or")
+ visible: root.showCreate
}
MMLinkButton {
width: parent.width - 2 * root.hPadding
text: qsTr("Create new workspace")
+ visible: root.showCreate
onClicked: root.createWorkspaceClicked()
}
diff --git a/app/qml/onboarding/MMAcceptInvitationController.qml b/app/qml/onboarding/MMAcceptInvitationController.qml
new file mode 100644
index 000000000..f53f4252c
--- /dev/null
+++ b/app/qml/onboarding/MMAcceptInvitationController.qml
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+import QtQuick
+
+import "../"
+import QtQuick.Controls
+import lc 1.0
+
+/**
+ * Show accept invitation page directly without onboarding
+ * e.g. on app start
+ */
+Item {
+ id: controller
+
+ required property bool enabled
+ required property var stackView
+
+ property bool showInvitationList: false // TODO merge with enabled ?
+
+ Connections {
+ id: openInvitationsListener
+
+ target: __merginApi
+ enabled: root.enabled && root.showInvitationList
+
+ function onUserInfoReplyFinished() {
+ // controller.showInvitationsList = false;
+
+ /*
+ it should be enabled = false in this case from parent!
+
+ // let's not show invitations when registration finish page is opened
+ if ( stackView.containsPage("registrationFinishPanel") ) {
+ return;
+ }
+ */
+
+ if ( !__merginApi.userAuth.hasAuthData() ) {
+ return;
+ }
+
+ if ( __merginApi.userInfo.hasInvitations ) {
+ stackView.push( acceptInvitationsPanelComponent )
+ }
+ }
+ }
+
+ Connections {
+ target: __merginApi
+ enabled: root.enabled && root.showInvitationList
+
+ function onProcessInvitationFinished( accepted ) {
+ stackView.pop(null)
+ }
+ }
+
+ Component {
+ id: acceptInvitationsPanelComponent
+
+ MMAcceptInvitation {
+ objectName: "acceptInvitationsPanelDirect"
+ haveBack: true
+ showCreate: false
+
+ onBackClicked: {
+ stackView.popOnePageOrClose()
+ }
+
+ onJoinWorkspaceClicked: function (workspaceUuid) {
+ __merginApi.processInvitation( workspaceUuid, true )
+ }
+ }
+ }
+}
diff --git a/app/qml/onboarding/MMCreateWorkspace.qml b/app/qml/onboarding/MMCreateWorkspace.qml
index ed9fe15bd..a7521c625 100644
--- a/app/qml/onboarding/MMCreateWorkspace.qml
+++ b/app/qml/onboarding/MMCreateWorkspace.qml
@@ -16,12 +16,17 @@ import "../inputs"
Page {
id: root
- signal continueClicked
+ signal createWorkspaceClicked(string name)
readonly property real hPadding: width < __style.maxPageWidth
? 20 * __dp
: (20 + (width - __style.maxPageWidth) / 2) * __dp
+ // show error message under the respective field
+ function showErrorMessage( msg ) {
+ workspaceName.errorMsg = msg
+ }
+
Rectangle {
anchors.fill: parent
color: __style.lightGreenColor
@@ -81,6 +86,7 @@ Page {
Item { width: 1; height: 1 }
MMInputEditor {
+ id: workspaceName
width: parent.width - 2 * root.hPadding
title: qsTr("Workspace name")
placeholderText: qsTr("Your Workspace")
@@ -109,7 +115,7 @@ Page {
width: parent.width - 2 * root.hPadding
text: qsTr("Create workspace")
- onClicked: root.continueClicked()
+ onClicked: root.createWorkspaceClicked(workspaceName.text)
}
}
}
diff --git a/app/qml/onboarding/MMCreateWorkspaceController.qml b/app/qml/onboarding/MMCreateWorkspaceController.qml
new file mode 100644
index 000000000..29922967e
--- /dev/null
+++ b/app/qml/onboarding/MMCreateWorkspaceController.qml
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+import QtQuick
+
+import "../"
+import QtQuick.Controls
+import lc 1.0
+
+/**
+ * Directly create workspace but without onboarding questions
+ * e.g. from account page or projects panel
+ */
+Item {
+ id: controller
+
+ required property var stackView
+ required property bool enabled
+
+ Connections {
+ target: __merginApi
+ enabled: controller.enabled
+
+ function onWorkspaceCreated(workspace, result) {
+ if (result) {
+ stackView.pop("createWorkspaceDirectPanel")
+ }
+ }
+ }
+
+ Component {
+ id: createWorkspaceComponent
+
+ MMCreateWorkspace {
+ id: createWorkspacePanel
+
+ objectName: "createWorkspaceDirectPanel"
+
+ onCreateWorkspaceClicked: function (workspaceName) {
+ __merginApi.createWorkspace(workspaceName)
+ }
+ }
+ }
+}
diff --git a/app/qml/onboarding/MMHowYouFoundUs.qml b/app/qml/onboarding/MMHowYouFoundUs.qml
index d2681eace..6fa1e821f 100644
--- a/app/qml/onboarding/MMHowYouFoundUs.qml
+++ b/app/qml/onboarding/MMHowYouFoundUs.qml
@@ -21,7 +21,7 @@ Page {
property string selectedText: ""
signal backClicked
- signal continueClicked(var selectedText)
+ signal howYouFoundUsSelected(var selectedText)
readonly property string headerTitle: qsTr("How did you learn about us?")
readonly property real hPadding: width < __style.maxPageWidth
@@ -136,6 +136,9 @@ Page {
anchors.bottomMargin: 20 * __dp
text: qsTr("Continue")
- onClicked: root.continueClicked(root.selectedText)
+ onClicked: {
+ // TODO not allow in case there is missing selected text!
+ root.howYouFoundUsSelected(root.selectedText)
+ }
}
}
diff --git a/app/qml/onboarding/MMOnboardingController.qml b/app/qml/onboarding/MMOnboardingController.qml
index 412ecfa59..753d3f839 100644
--- a/app/qml/onboarding/MMOnboardingController.qml
+++ b/app/qml/onboarding/MMOnboardingController.qml
@@ -9,54 +9,41 @@
import QtQuick
import "../"
+import QtQuick.Controls
+import lc 1.0
-QtObject {
+Item {
id: controller
required property bool enabled
required property var stackView
+ property bool inProgrees: false
+
QtObject {
- id: privateProps
+ //! Data to send to postRegister endpoint
+ id: postRegisterData
- property bool useOnboarding: true
- property bool showInvitationsList: false
+ property bool wantNewsletter: false
+ property string howYouFoundUs: "" // one of the category of "other"
+ property string whichIndustry: "" // one of the category of "other"
}
- function showInvitationList()
+ // Start onboarding
+ function start()
{
- privateProps.showInvitationsList = true
- }
+ if (controller.inProgress)
+ return;
- // Create workspace but without onboarding questions
- function createNewWorkspace()
- {
- privateProps.useOnboarding = false
- stackView.push( createWorkspaceComponent )
- }
-
- // Create workspace but without onboarding questions
- function createNewWorkspace()
- {
- privateProps.useOnboarding = false
- stackView.push( createWorkspaceComponent )
+ controller.inProgress = true;
+ stackView.push( loginPageComp, {}, StackView.PushTransition )
}
- // Show login page
- function showLogin()
+ // Finish onboarding
+ function end()
{
- privateProps.useOnboarding = true
-
- for ( let i = 0; i < stackView.depth; i++ ) {
- let item = stackView.get( i )
-
- if ( item && item.objectName && item.objectName === "loginPage" ) {
- // sorry, it is already opened, let's not open it again
- return;
- }
- }
-
- stackView.push( loginPageComp, {}, StackView.PushTransition )
+ controller.inProgress = false;
+ stackView.pop(null);
}
Connections {
@@ -65,45 +52,19 @@ QtObject {
function onRegistrationFailed( msg, field ) {
stackView.pending = false
- if ( stackView.currentItem.objectName === "registrationPanel" ) {
+ if ( stackView.currentItem.objectName === "signUpPanel" ) {
stackView.currentItem.showErrorMessage(msg, field)
}
}
function onRegistrationSucceeded() {
stackView.pending = false
- stackView.push( registrationFinishComponent )
+ stackView.pushPage(createWorkspaceComponent)
}
function onWorkspaceCreated(workspace, result) {
if (result) {
- stackView.popPage("createWorkspacePanel")
- }
- }
- }
-
- Connections {
- id: openInvitationsListener
-
- property bool showInvitationsList: true
-
- target: __merginApi
- enabled: __merginApi.apiSupportsWorkspaces && openInvitationsListener.showInvitationsList
-
- function onUserInfoReplyFinished() {
- openInvitationsListener.showInvitationsList = false;
-
- // let's not show invitations when registration finish page is opened
- if ( stackView.containsPage("registrationFinishPanel") ) {
- return;
- }
-
- if ( !__merginApi.userAuth.hasAuthData() ) {
- return;
- }
-
- if ( __merginApi.userInfo.hasInvitations ) {
- stackView.push( invitationsPanelComponent )
+ stackView.pushPage(howYouFoundUsComponent)
}
}
}
@@ -156,7 +117,7 @@ QtObject {
Qt.openUrlExternally( __merginApi.apiRoot )
}
else {
- stackView.push( registrationPanel )
+ stackView.push( signUpPanel )
}
}
@@ -172,11 +133,11 @@ QtObject {
}
Component {
- id: registrationPanel
+ id: signUpPanel
MMSignUp {
- objectName: "registrationPanel"
+ objectName: "signUpPanel"
tocString: qsTr("I accept the Mergin %1Terms and Conditions%3 and %2Privacy Policy%3")
.arg("")
.arg("")
@@ -193,7 +154,7 @@ QtObject {
stackView.popOnePageOrClose()
}
- onSignUpClicked: function ( username, email, password, passwordConfirm, tocAccept ) {
+ onSignUpClicked: function ( username, email, password, passwordConfirm, tocAccept, newsletterSubscribe ) {
if ( __merginApi.serverType !== MerginServerType.SAAS ) {
return; //should not happen
}
@@ -204,6 +165,8 @@ QtObject {
password,
passwordConfirm,
tocAccept )
+
+ postRegisterData.wantNewsletter = newsletterSubscribe
}
}
}
@@ -212,35 +175,79 @@ QtObject {
Component {
id: createWorkspaceComponent
- CreateWorkspacePage {
+ MMCreateWorkspace {
id: createWorkspacePanel
objectName: "createWorkspacePanel"
- onBack: {
+ onCreateWorkspaceClicked: function (workspaceName) {
+ __merginApi.createWorkspace(workspaceName)
+ }
+ }
+ }
+
+ Component {
+ id: howYouFoundUsComponent
+
+ MMHowYouFoundUs {
+ id: howYouFoundUsPanel
+
+ objectName: "howYouFoundUsPanel"
+ onBackClicked: {
+ stackView.popOnePageOrClose()
+ }
+
+ onHowYouFoundUsSelected: function (selectedText) {
+ postRegisterData.howYouFoundUs = selectedText
+ stackView.push(whichIndustryComponent)
+ }
+ }
+ }
+
+ Component {
+ id: whichIndustryComponent
+
+ MMWhichIndustry {
+ id: whichIndustryPanel
+
+ objectName: "whichIndustryPanel"
+
+ onBackClicked: {
stackView.popOnePageOrClose()
}
+
+ onIndustrySelected: function (selectedText) {
+ postRegisterData.whichIndustry = selectedText
+ console.log("TODO: finished onboarding?? - CALL postRegister/")
+ }
}
}
Component {
- id: invitationsPanelComponent
+ id: acceptInvitationsPanelComponent
+
+ MMAcceptInvitation {
+ objectName: "acceptInvitationsPanel"
+ haveBack: false
+ showCreate: true
- ManageInvitationsPage {
- objectName: "invitationsPanel"
- haveBack: true
- showCreate: false
- onBack: {
- stackView.pop( null )
+ onJoinWorkspaceClicked: function (workspaceUuid) {
+ __merginApi.processInvitation( workspaceUuid, true )
+ }
+
+ onCreateWorkspaceClicked: {
+ if (stackView.containsPage("createWorkspacePanel"))
+ {
+ stackView.popOnePageOrClose()
+ } else {
+ stackView.push(createWorkspaceComponent)
+ }
}
Connections {
target: __merginApi
function onProcessInvitationFinished( accepted ) {
- stackView.pop(null)
- if ( __merginApi.userInfo.hasWorkspaces && accepted ) {
- stackView.push(workspaceListComponent)
- }
+ controller.end()
}
}
}
diff --git a/app/qml/onboarding/MMSignUp.qml b/app/qml/onboarding/MMSignUp.qml
index 03f59f0b1..9c1e4bd29 100644
--- a/app/qml/onboarding/MMSignUp.qml
+++ b/app/qml/onboarding/MMSignUp.qml
@@ -29,7 +29,7 @@ Page {
signal backClicked
signal signInClicked
- signal signUpClicked ( string username, string email, string password, string passwordConfirm, bool tocAccept )
+ signal signUpClicked ( string username, string email, string password, string passwordConfirm, bool tocAccept, bool newsletterSubscribe )
required property string tocString
readonly property real hPadding: width < __style.maxPageWidth
@@ -44,7 +44,7 @@ Page {
email.errorMsg = ""
password.errorMsg = ""
passwordConfirm.errorMsg = ""
- tocAccept.errorMsg = ""
+ // TODO tocAccept.errorMsg = ""
// TODO errorText.text = ""
if( field === RegistrationError.USERNAME ) {
@@ -64,8 +64,10 @@ Page {
passwordConfirm.focus = true
}
else if( field === RegistrationError.TOC ) {
- tocAccept.errorMsg = msg
- tocAccept.focus = true
+ // TODO where to show MMCheckBox missing errorMsg
+ // tocAccept.errorMsg = msg
+ // tocAccept.focus = true
+ console.log("error " + msg)
}
else if( field === RegistrationError.OTHER ) {
// TODO where to show
@@ -181,6 +183,30 @@ Page {
}
}
+ Row {
+ width: parent.width
+ spacing: 10 * __dp
+
+ MMCheckBox {
+ id: newsletterSubscribe
+
+ width: 24 * __dp
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ Text {
+ width: parent.width - newsletterSubscribe.width - parent.spacing - 2 * root.hPadding
+ anchors.verticalCenter: parent.verticalCenter
+
+ text: qsTr("I want to subscribe to the newsletter")
+ font: __style.p5
+ color: __style.nightColor
+ linkColor: __style.forestColor
+ wrapMode: Text.WordWrap
+ lineHeight: 1.5
+ }
+ }
+
Item { width: 1; height: 1 }
MMButton {
@@ -193,7 +219,8 @@ Page {
email.text,
password.text,
passwordConfirm.text,
- tocAccept.checked
+ tocAccept.checked,
+ newsletterSubscribe.checked
)
}
}
diff --git a/app/qml/onboarding/MMWhichIndustry.qml b/app/qml/onboarding/MMWhichIndustry.qml
index 628a4ba5e..e8a0bbd9c 100644
--- a/app/qml/onboarding/MMWhichIndustry.qml
+++ b/app/qml/onboarding/MMWhichIndustry.qml
@@ -21,7 +21,7 @@ Page {
property string selectedText: ""
signal backClicked
- signal continueClicked(var selectedText)
+ signal industrySelected(var selectedText)
readonly property string headerTitle: qsTr("In which industry do you work?")
readonly property real hPadding: width < __style.maxPageWidth
@@ -142,6 +142,9 @@ Page {
anchors.bottomMargin: 20 * __dp
text: qsTr("Continue")
- onClicked: root.continueClicked(root.selectedText)
+ onClicked: {
+ // TODO not allow in case there is missing selected text!
+ root.industrySelected(root.selectedText)
+ }
}
}