Skip to content

Instantly share code, notes, and snippets.

@shannah
Created June 24, 2021 13:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shannah/3df2ea9184fd2f2f8ab80e219c73b5c0 to your computer and use it in GitHub Desktop.
Save shannah/3df2ea9184fd2f2f8ab80e219c73b5c0 to your computer and use it in GitHub Desktop.
TwitterClone state after adding the Signup Page
<?xml version="1.0"?>
<border view-controller="com.example.tweetapp.controllers.SignupPageViewController"
uiid="SignupPage"
safeArea="true"
xsi:noNamespaceSchemaLocation="SignupPage.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<define-category name="NEXT"/>
<define-category name="USE_PHONE"/>
<define-category name="USE_EMAIL"/>
<define-category name="ENTER_PHONE_OR_EMAIL"/>
<define-tag name="name" value="Person.name"/>
<define-tag name="phone" value="Person.telephone"/>
<define-tag name="email" value="Person.email"/>
<define-tag name="birthDate" value="Person.birthDate" type="java.util.Date"/>
<define-tag name="useEmail" type="boolean"/>
<define-tag name="editingPhoneOrEmail" type="boolean"/>
<define-tag name="phoneFieldFocused" type="boolean"/>
<define-tag name="emailFieldFocused" type="boolean"/>
<!-- Properties for error messages -->
<define-tag name="nameErrorMessage"/>
<define-tag name="phoneOrEmailErrorMessage"/>
<define-tag name="birthDateErrorMessage"/>
<title>
<label iconUIID="TwitterIcon" fontIcon="(char)0xe902" ></label>
</title>
<y layout-constraint="center" uiid="SignupPageContent" scrollableY="true">
<label uiid="SignupPageTitle">Create your account</label>
<radTextField
tag="name"
component.hint="Name"
component.uiid="TwitterTextField"
component.hintLabel.uiid="TwitterTextFieldHint"
/>
<radLabel tag="nameErrorMessage"
bind-hidden="${nameErrorMessage}.isEmpty()"
rad-transition="hidden 0.3s"
component.uiid="FieldErrorMessage"
/>
<button uiid="PhoneOrEmailButton"
text="Phone number or email address"
bind-hidden="${editingPhoneOrEmail}.bool"
>
<script>
it.addActionListener(evt -> {
evt.consume();
it.getComponentForm().setFormBottomPaddingEditingMode(true);
${editingPhoneOrEmail}.setBoolean(true);
if (${useEmail}.bool) {
emailTextField.getComponent().startEditingAsync();
} else {
phoneTextField.getComponent().startEditingAsync();
}
});
</script>
</button>
<radTextField
rad-var="phoneTextField"
tag="phone"
bind-component.focus="phoneFieldFocused"
bind-hidden="${useEmail}.bool || !${editingPhoneOrEmail}.bool"
component.hint="Phone number"
component.uiid="TwitterTextField"
component.hintLabel.uiid="TwitterTextFieldHint"
component.constraint="TextArea.PHONENUMBER"
/>
<radTextField
rad-var="emailTextField"
bind-hidden="!${useEmail}.bool || !${editingPhoneOrEmail}.bool"
tag="email"
component.hint="Email address"
component.uiid="TwitterTextField"
component.hintLabel.uiid="TwitterTextFieldHint"
component.constraint="TextArea.EMAILADDR"
bind-component.focus="emailFieldFocused"
>
</radTextField>
<radLabel tag="phoneOrEmailErrorMessage"
bind-hidden="${phoneOrEmailErrorMessage}.isEmpty()"
rad-transition="hidden 0.3s"
component.uiid="FieldErrorMessage"
/>
<radDatePicker
tag="birthDate"
component.text="Date of birth"
component.uiid="TwitterDatePicker"
/>
<radLabel tag="birthDateErrorMessage"
bind-hidden="${birthDateErrorMessage}.isEmpty()"
rad-transition="hidden 0.3s"
component.uiid="FieldErrorMessage"
/>
</y>
<border layout-constraint="south" uiid="SignupPageSouth">
<x layout-constraint="west">
<button text="Use Email Address"
bind-hidden="!${phoneFieldFocused}.bool"
uiid="TextFieldToggleButton"
>
<script>
it.addActionListener(evt->{
${useEmail}.setBoolean(true);
emailTextField.startEditingAsync();
});
</script>
</button>
<button text="Use Phone"
bind-hidden="!${emailFieldFocused}.bool"
uiid="TextFieldToggleButton"
>
<script>
it.addActionListener(evt->{
${useEmail}.setBoolean(false);
phoneTextField.startEditingAsync();
});
</script>
</button>
</x>
<x layout-constraint="east">
<button uiid="TwitterNextButton" text="Next">
<bind-action category="NEXT"/>
</button>
</x>
</border>
</border>
package com.example.tweetapp.controllers;
import com.codename1.components.InfiniteProgress;
import com.codename1.components.ToastBar;
import com.codename1.rad.controllers.Controller;
import com.codename1.rad.controllers.ViewController;
import com.codename1.rad.nodes.ActionNode;
import com.codename1.rad.util.NonNull;
import com.codename1.ui.Dialog;
import com.example.tweetapp.services.TweetAppClient;
import com.example.tweetapp.views.SignupPage;
import com.example.tweetapp.views.SignupPageModel;
import com.example.tweetapp.views.SignupPageModelWrapper;
import com.example.tweetapp.views.HomePageController;
public class SignupPageViewController extends ViewController {
/**
* Creates a new ViewController with the given parent controller.
*
* @param parent
*/
public SignupPageViewController(Controller parent) {
super(parent);
}
@Override
protected void initControllerActions() {
super.initControllerActions();
ActionNode.builder()
.addToController(this, SignupPage.NEXT, this::handleSubmit);
}
/**
* Handles the registration form submission
* @param evt
*/
private void handleSubmit(ActionNode.ActionNodeEvent evt) {
// Get reference to the view's model via the event.
SignupPageModel viewModel = SignupPageModelWrapper.wrap(evt.getEntity());
// Do some validation
boolean failedValidation = false;
if (viewModel.isUseEmail() && NonNull.empty(viewModel.getEmail())) {
viewModel.setPhoneOrEmailErrorMessage("Email address cannot be empty");
failedValidation = true;
} else if (!viewModel.isUseEmail() && NonNull.empty(viewModel.getPhone())) {
viewModel.setPhoneOrEmailErrorMessage("Phone cannot be empty");
failedValidation = true;
} else {
viewModel.setPhoneOrEmailErrorMessage("");
}
if (NonNull.empty(viewModel.getName())) {
viewModel.setNameErrorMessage("Name cannot be empty");
failedValidation = true;
} else {
viewModel.setNameErrorMessage("");
}
if (NonNull.empty(viewModel.getBirthDate())) {
viewModel.setBirthDateErrorMessage("Birthdate cannot be empty");
failedValidation = true;
} else {
viewModel.setBirthDateErrorMessage("");
}
if (failedValidation) {
return;
}
// Get reference to the webservice client
TweetAppClient client = lookup(TweetAppClient.class);
TweetAppClient.SignupRequest request = client.createSignupRequest()
.name(viewModel.getName())
.birthDate(viewModel.getBirthDate());
if (viewModel.isUseEmail()) {
request.email(viewModel.getEmail());
} else {
request.phone(viewModel.getPhone());
}
InfiniteProgress progess = new InfiniteProgress();
Dialog progressDialog = progess.showInfiniteBlocking();
request.signup().onResult((res, err) -> {
progressDialog.dispose();
if (err != null) {
ToastBar.showErrorMessage(err.getMessage());
return;
}
new HomePageController(getApplicationController()).show();
});
}
}
#Constants {
includeNativeBool: true;
defaultFontSizeInt:'18';
}
/* Global Overrides */
TitleArea {
border:none;
background-color:white;
margin:0;
}
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.ttf');
}
WelcomePage {
padding: 10vw;
background-color: white;
}
TwitterButton {
cn1-derive: Button;
background-color: #1DA1F2;
color: white;
border: cn1-pill-border;
padding: 0.7rem;
font-size:1rem;
}
TwitterHeading1 {
font-size: 1.5rem;
font-family: 'native:MainBold';
color: black;
margin-bottom: 1rem;
}
TwitterSmallLabel {
cn1-derive: Label;
font-size: 0.7rem;
font-family: 'native:MainRegular';
padding:0;
margin:0;
color: #66757f;
margin-right: 1mm;
}
TwitterSmallLink {
cn1-derive: Button;
font-size: 0.7rem;
font-family: 'native:MainRegular';
padding:0;
margin:0;
color: #1DA1F2;
}
TwitterIcon {
font-family: icomoon;
font-size: 1.4rem;
color: #1DA1F2;
}
/** Signup Page Styles */
SignupPage {
background-color:white;
margin:0;
}
SignupPageContent {
padding: 8vw;
}
SignupPageSouth {
padding:2mm;
padding-left: 8vw;
padding-right: 8vw;
margin:0;
border-top: 1px solid gray;
}
SignupPageTitle {
cn1-derive: Label;
font-size: 1.2rem;
font-family: "native:MainBold";
text-align:center;
margin-bottom: 1.7rem;
color: black;
}
TwitterNextButton {
cn1-derive: Button;
background-color: #1DA1F2;
color: white;
border: cn1-pill-border;
padding: 0.5rem 0.75rem;
font-size: 0.7rem;
margin-right: 0;
}
TwitterTextField, TwitterTextFieldHint {
padding-top: 0.7rem;
padding-bottom: 0.7rem;
font-size: 0.8rem;
font-family: "native:MainLight";
color: gray;
}
TwitterTextField {
cn1-derive: TextField;
border: none;
border-bottom: 0.8pt solid #ccc;
color: #333333;
margin-top: 1rem;
margin-bottom: 0.5mm;
}
TwitterDatePicker {
cn1-derive: TwitterTextField;
color: gray;
}
PhoneOrEmailButton {
cn1-derive: TwitterTextField;
color: gray;
}
TwitterTextFieldHint {
color: #66757f;
}
TextFieldToggleButton {
cn1-derive: Button;
color: #1DA1F2;
border: none;
padding: 0.5rem 0.75rem;
font-size: 0.7rem;
margin:0;
}
FieldErrorMessage {
cn1-derive: Label;
font-size: 0.7rem;
color: white;
background-color:red;
padding: 1.5mm;
margin-top: 0;
}
package com.example.tweetapp;
import static com.codename1.rad.util.NonNull.with;
import static com.codename1.ui.CN.*;
import com.codename1.rad.controllers.ControllerEvent;
import com.codename1.rad.nodes.ActionNode;
import com.codename1.rad.ui.ActionStyle;
import com.codename1.ui.*;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.layouts.*;
import com.codename1.io.*;
import com.codename1.ui.plaf.*;
import com.codename1.ui.util.Resources;
import com.codename1.rad.controllers.ApplicationController;
import com.example.tweetapp.services.TweetAppClient;
import com.example.tweetapp.views.WelcomePageController;
/**
* This file was generated by <a href="https://www.codenameone.com/">Codename One</a> for the purpose
* of building native mobile applications using Java.
*/
public class Tweetapp extends ApplicationController {
@Override
protected void initControllerActions() {
super.initControllerActions();
}
@Override
protected void onStartController() {
super.onStartController();
/**
* Add a TweetAppClient as a lookup so that it will be available throughout
* the app via {@link #lookup(Class)}
*/
addLookup(new TweetAppClient());
}
public void actionPerformed(ControllerEvent evt) {
with(evt, StartEvent.class, startEvent -> {
if (!startEvent.isShowingForm()) {
startEvent.setShowingForm(true);
new WelcomePageController(this).show();
}
});
super.actionPerformed(evt);
}
}
package com.example.tweetapp.services;
import com.codename1.rad.util.NonNull;
import com.codename1.util.AsyncResource;
import java.util.Date;
/**
* A client for interacting with the server.
*/
public class TweetAppClient {
/**
* Flag to indicate that we are currently logged in.
*/
private boolean loggedIn;
/**
* The currently logged in user Id. In this mock implementation the user Id
* is just the email address or phone number.
*/
private String loggedInUserId;
public boolean isLoggedIn(){
return loggedIn;
}
public String getLoggedInUserId() {
return loggedInUserId;
}
/**
* A response object that is passed to the SignupRequest callback
* upon completion.
*/
public static class SignupResponse {
/**
* Whether the signup was successful
*/
private boolean success;
/**
* Reference to request that this response is for.
*/
private SignupRequest request;
/**
* The response code. 200 for success.
* Make up error codes to fit needs.
*/
private int responseCode;
/**
* A message related to the response code. Contains error message
* in case of errors.
*/
private String message;
public boolean isSuccess() {
return success;
}
public SignupRequest getRequest() {
return request;
}
public int getResponseCode() {
return responseCode;
}
public String getMessage() {
return message;
}
}
/**
* Encapsulates a signup request to send to the server. Modify this
* class to include the information you require in your signup process.
*
*/
public class SignupRequest extends AsyncResource<SignupResponse> {
/**
* The email address of the user.
*/
private String email,
/**
* The phone number of the user.
*/
phone,
/**
* The name of the user.
*/
name;
/**
* The birth date of the user.
*/
private Date birthDate;
/**
* Send the signup request.
* @return
*/
public SignupRequest signup() {
return TweetAppClient.this.signup(this);
}
public SignupRequest email(String email) {
this.email = email;
return this;
}
public SignupRequest phone(String phone) {
this.phone = phone;
return this;
}
public SignupRequest birthDate(Date birthDate) {
this.birthDate = birthDate;
return this;
}
public SignupRequest name(String name) {
this.name = name;
return this;
}
}
/**
* Creates a new signup request.
* @return
*/
public SignupRequest createSignupRequest() {
return new SignupRequest();
}
/**
* Sends a signup request to the server.
* @param request
* @return
*/
private SignupRequest signup(SignupRequest request) {
// This is just mocking the signup process.
// Change this to contact the server and sign up.
SignupResponse response = new SignupResponse();
response.responseCode = 200;
response.message = "Success";
response.request = request;
response.success = true;
request.complete(response);
// To log in we set the loggedInUserId and loggedIn
// boolean flag.
loggedInUserId = NonNull.nonNull(request.email, request.phone);
loggedIn = true;
return request;
}
}
<?xml version="1.0"?>
<borderAbsolute
safeArea="true"
uiid="WelcomePage"
xsi:noNamespaceSchemaLocation="WelcomePage.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<title hidden="true"/>
<center layout-constraint="north">
<label iconUIID="TwitterIcon" fontIcon="(char)0xe902" ></label>
</center>
<y layout-constraint="center" uiid="TwitterContentPane">
<spanLabel textUIID="TwitterHeading1">See what's happening in the world right now.</spanLabel>
<button uiid="TwitterButton" rad-href="#SignupPage">Create account</button>
</y>
<flow layout-constraint="south">
<label uiid="TwitterSmallLabel">Have an account already?</label>
<button uiid="TwitterSmallLink">Log in</button>
</flow>
</borderAbsolute>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment