B4J Tutorial [ABMaterial]: Creating a Sign In Modal Dialog with Options: Part 2

Hi again

This is the second installment of the above mentioned article. In this part we will look into the "Forgot Password" UX design and some additional things.

Part 2.1


Goal

  • When a user selects Forgot Password in the Sign In screen, the Forgot Password screen should appear.
  • The email entered by the user is compulsory. After the user enters their email address and selects Next
  • The email will be read from an existing user table and it found should be sent to the user via email.
  • The email settings for the smtp should be read from a pre-defined table and used to initialize smtp.
  • The user is provided an option to Try something else, this activates a screen to Change the password.

Reproduction

Creating a modal sheet is simple as discussed in the previous article about this activity.

1.1 Creating this Forgot Password Modal Sheet.

B4X:
Private Sub contLoginBuild() As ABMModalSheet
    Dim contLogin As ABMModalSheet
    contLogin.Initialize(page, "contLogin", False, ABM.MODALSHEET_TYPE_NORMAL, "ms")
    contLogin.Size = ABM.MODALSHEET_SIZE_NORMAL
    contLogin.IsDismissible = False
    contLogin.IsTextSelectable = True
    contLogin.Content.AddRows(1,True,"").AddCells12(1, "")
    contLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
    contLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 10, 0, ABM.VISIBILITY_ALL, "")
    contLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, ABM.VISIBILITY_ALL, "")
    contLogin.Content.AddRows(1,True,"").AddCellsOSMPV(1, 0, 0, 0, 6, 6, 6, 0, 0, 10, 0, ABM.VISIBILITY_ALL, "").AddCellsOSMPV(1, 0, 0, 0, 6, 6, 6, 0, 0, 10, 10, ABM.VISIBILITY_ALL, "")
    contLogin.Header.BuildGrid  'IMPORTANT once you loaded the complete grid AND before you start adding components
    contLogin.Content.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
    contLogin.Footer.BuildGrid  'IMPORTANT once you loaded the complete grid AND before you start adding components
    'Add components to ModalSheet
    Dim lblTrySomethingElse As ABMLabel
    lblTrySomethingElse.Initialize(page, "lblTrySomethingElse", $"{C:#993333}{I}Try something else?{/I}{/C}"$, ABM.SIZE_PARAGRAPH, False, "")
    lblTrySomethingElse.Clickable = True
    lblTrySomethingElse.SetCursor(ABM.CURSOR_POINTING)
    lblTrySomethingElse.istextselectable = True
    lblTrySomethingElse.supportemoji = False
    contLogin.Content.Cell(5,1).AddComponent(lblTrySomethingElse)
    Dim imglogo As ABMImage
    imglogo.Initialize(page, "imglogo", "../images/logo.png?"& DateTime.Now, 1)
    imglogo.Tag = "imglogo"
    imglogo.IsResponsive = False
    imglogo.IsCircular = False
    imglogo.IsClickable = False
    imglogo.IsMaterialBoxed = False
    contLogin.Content.Cell(2,1).AddComponent(imglogo)
    Dim txtemail As ABMInput
    txtemail.Initialize(page, "txtemail", ABM.INPUT_EMAIL, "Email Address", False, "")
    txtemail.Tag = "txtemail"
    contLogin.Content.Cell(4,1).AddComponent(txtemail)
    Dim btnNext As ABMButton
    btnNext.InitializeRaised(page, "btnNext", "", "", "Next", "")
    btnNext.Tag = "btnNext"
    btnNext.UseFullCellWidth = True
    btnNext.Size = ABM.BUTTONSIZE_NORMAL
    btnNext.dropdownhidearrow = False
    btnNext.dropdownshowbelow = False
    btnNext.dropdownconstrainwidth = False
    contLogin.Content.Cell(5,2).AddComponent(btnNext)
    Dim lblheader As ABMLabel
    lblheader.Initialize(page, "lblheader", $"{B}Account help{/B}"$, ABM.SIZE_PARAGRAPH, False, "")
    lblheader.istextselectable = True
    lblheader.supportemoji = False
    contLogin.Content.Cell(3,1).AddComponent(lblheader)
    Dim lblsubheader As ABMLabel
    lblsubheader.Initialize(page, "lblsubheader", $"If {B}registered{/B} with us, your password will be emailed"$, ABM.SIZE_PARAGRAPH, False, "")
    lblsubheader.istextselectable = True
    lblsubheader.supportemoji = False
    contLogin.Content.Cell(3,1).AddComponent(lblsubheader)
    Return contLogin
End Sub

Next we call this method on BuildPage and then show the ModalSheet when the page is connected. As the email is compulsory we need to validate it too.

B4X:
'Get the contents of the modal sheet input components and save to a map
Sub contLoginGetContents() As Map
    Dim pMap As Map
    pMap.Initialize
    Dim contLogin As ABMModalSheet
    contLogin = page.ModalSheet("contLogin")
    Dim txtemail As ABMInput = contLogin.Content.Component("txtemail")
    pMap.put("email", txtemail.Text)
    Return pMap
End Sub
'Get the contents of the modal sheet from GetContents and check validity
Sub contLoginValidate(gMap As Map) As Boolean
    Dim contLogin As ABMModalSheet
    contLogin = page.ModalSheet("contLogin")
    Dim stxtemail As String
    stxtemail = gMap.get("email")
    If stxtemail = "null" Then stxtemail = ""
    If stxtemail.Length = 0 Then
        ABMShared.Warn(page,"Email Address cannot be blank. Please enter a value.")
        Dim txtemail As ABMInput = contLogin.Content.Component("txtemail")
        txtemail.SetFocus
        Return False
    End If
    Return True
End Sub

As soon as the Next Button is selected, the process of verifying the entered email address against the database starts.

B4X:
'An ABMButton has been clicked
Sub btnNext_Clicked(Target As String)
    Wait For (contLoginForgotPassword) Complete(Result As Boolean)
    If Result = False Then Return
    ABMShared.NavigateToPage(ws, ABMPageId, "../frmTellForgotPassword/frmTellForgotPassword.html")
End Sub

the contLoginForgotPassword sub is called. I decided to try out a ResumableSub inside an ABMaterial project. The code compiles well.

B4X:
Private Sub contLoginForgotPassword() As ResumableSub
    'define a map to hold the form contents
    Dim m As Map
    'read the form contents to a map
    m = contLoginGetContents
    Dim email As String
    email = m.get("email")
    If email = "null" Then email = ""
    If email.Length = 0 Then
        ABMShared.Warn(page,"Email cannot be blank. Please enter a value.")
        Return False
    End If
    'the form contents are ok, send login credentials to user
    'define the search criteria, fields must equal
    Dim w As Map
    Dim nk As String
    w.Initialize
    nk = "email="
    w.put(nk, email)
    'Get connection from current pool if MySQL/MSSQL
    Dim jSQL As SQL = ABMShared.SQLGet
    Dim UserMap As Map = ABMShared.SQLSelectRecordWhereMap(jSQL,"users", w)
    If UserMap.IsInitialized = False Then
        ABMShared.Warn(page,"The Email specified could not be found in our records, please specify the correct email.")
        Return False
    End If
    'The email has been found, send the details to the user
    Dim username As String
    username = UserMap.get("username")
    Dim password As String
    password = UserMap.get("password")
    Try
        ' start smtp to send the emails
        Dim sbody As String = "Good Day||Someone or you requested your Sign In credentials.||Your UserName is: " & username & "||Your Password is: " & password & "||Development Team|"
        sbody = ABMShared.Replace(sbody,"|",CRLF)
        Wait For (ABMShared.SendEmail(email, "askteencoach: Sign In Credentials", sbody)) Complete(Result As Boolean)
        If Result = False Then
            myToastId = myToastId + 1
            page.ShowToast("toast" & myToastId, "toastred", "Sign In Credentials could not be emailed!", 3000,False)
        Else
            myToastId = myToastId + 1
            page.ShowToast("toast" & myToastId, "toastgreen", "Sign In Credentials emailed successfully.", 3000,False)
        End If
        'Close the connection to the database
        ABMShared.SQLClose(jSQL)
        Return Result
    Catch
        myToastId = myToastId + 1
        page.ShowToast("toast" & myToastId, "toastred", "Email could not be sent, please try again.", 3000,False)
        Return False
    End Try
End Sub

The above code, gets the contents of the modal sheet, checks if its valid and also verifies it against a database. When the user record is found that matches the email address, the password is read and these are sent to the user via email. The sub call is SendEmail saved in ABMShared.

B4X:
Sub GetEmailSettings() As Map
    Dim es As Map
    es.Initialize
    Dim jSQL As SQL = SQLGet
    'get first available account, there should be 1
    Dim id As String = SQLSelectSingleResult(jSQL, "select id from EmailAccounts where active = 1", Null)
    If id = "" Then Return es
    'read the email settings first
    es = SQLRecordRead(jSQL,"EmailAccounts", "id", id)
    Return es
End Sub
Sub SendEmail(toEmail As String, subject As String, msg As String) As ResumableSub
    Try
        'read the email settings first
        Dim es As Map = GetEmailSettings
        Dim sserver As String = es.GetDefault("server","")
        Dim sport As String = es.GetDefault("port","25")
        Dim susername As String = es.GetDefault("username","")
        Dim spassword As String = es.GetDefault("password","")
        Dim sStartTLSMode As String = es.GetDefault("starttlsmode","0")
        Dim sUseSSL As String = es.GetDefault("usessl","0")
        'start sending the email
        esmtp.Initialize(sserver, sport, susername, spassword, "smtp")
        esmtp.To.Add(toEmail)
        esmtp.Body = msg
        esmtp.Subject = subject
        If sStartTLSMode = "1" Then
            esmtp.StartTLSMode = True
        Else
            esmtp.StartTLSMode = False
        End If
        If sUseSSL = "1" Then
            esmtp.UseSSL = True
        Else
            esmtp.UseSSL = False
        End If
        Wait For (esmtp.Send) smtp_MessageSent(Success As Boolean)
        Return Success
    Catch
        Return False
    End Try
End Sub
Sub smtp_MessageSent(Success As Boolean)
    Log(Success)
End Sub

The EmailAccounts table stores settings for email accounts that can be used to send emails in our app. There can only be 1 active account to send emails from at anytime. The settings are read from that table, these provide the smtp server, the port, the username and password to use to send the email.

I usually test these settings via b4j first and then save them as default in my webapp.

The option to try something else has been provided to enable the user to change their password, should they remember it. This will be Part 2.1 of this article.

Disclaimer:

This article is mostly about UX design with specific attention to ModalSheets for this purpose. A try to work with ResumableSub was undertaken here to send the email to the user. For now its still a dololo experience. #investigating. The header/footer components of the ModalSheet are not used for this exercise. The adding of the components on the modal sheet is not sequential (this is not recommended) as on ConnectPage, the last modalsheet element is added then others added after. ;)

Related Article: Enabling Users to get their lost passwords


Ta!
 

Attachments

  • frmForgotPassword.bas
    18.9 KB · Views: 455
Last edited:
Top