Introduction to FIDO Authentication and Passkeys.pptx
Miscellaneous Features - Part 1 - Transcript.pdf
1. Miscellaneous Features - Part I
In this section we’ll cover a lot of minor features mostly related to UI that are part of the missing pieces on the road to complete the app
4. CategoryEntity ce = null;
for(CategoryEntity current : e.getCategories()) {
if(current.getName().equals(d.getCategory())) {
ce = current;
break;
}
}
if(ce == null && d.getCategory() != null) {
ce = new CategoryEntity(d.getCategory());
categoryRepository.save(ce);
}
CategoryEntity oe = de.getCategory();
de.setCategory(ce);
dishRepository.save(de);
if(d.getId() == null) {
Set<DishEntity> dishes = new HashSet<>(e.getDishes());
dishes.add(de);
e.setDishes(dishes);
restaurantRepository.save(e);
}
if(oe != null) {
cullCategory(e, oe);
}
Server - DishService Put Dish
Here we can see the server code for the categories and you will notice that it is handled as part of the dish update. There is no separate webservice for categories further
simplifying this logic.
We implicitly create a category if it’s missing and save it to the mysql database. We have a separate call to cull the category which we’ll go into now
5. private void cullCategory(RestaurantEntity e, CategoryEntity ce) {
for(DishEntity de : e.getDishes()) {
if(ce.equals(de.getCategory())) {
return;
}
}
categoryRepository.delete(ce);
}
@RequestMapping(method = RequestMethod.DELETE)
public @ResponseBody String delete(@RequestBody(required = true) Dish dsh) throws IOException {
RestaurantEntity e = restaurantRepository.findBySecret(dsh.getSecret());
for(DishEntity d : e.getDishes()) {
if(d.getId().equals(dsh.getId())) {
HashSet<DishEntity> de = new HashSet<>(e.getDishes());
de.remove(d);
e.setDishes(de);
e.setDishListUpdateTimestamp(System.currentTimeMillis());
restaurantRepository.save(e);
d.setDeleteTime(System.currentTimeMillis());
if(d.getCategory() != null) {
CategoryEntity ce = d.getCategory();
d.setCategory(null);
dishRepository.save(d);
cullCategory(e, ce);
} else {
dishRepository.save(d);
}
Server - cullCategory
The cull category call just loops over the dishes to see if a category is unused. If it is missing the method deletes that category.
The delete method also culls the category since the deleted dish might be the last one using a specific category so we need the call there too.
6. Validator val = new Validator();
val.addConstraint(category,
new LengthConstraint(1, "Category is required"));
val.addConstraint(price,
new NumericConstraint(true, 0, 1000000,
"Price must be a positive number"));
val.addSubmitButtons(ok);
Validation
Back in the client we use the validator API to place a constraint on the category so we’ll always have a category for a dish. We also set a numeric constraint on the price.
Notice that a validator is essential even if you set the text field as a numeric text field. That definition only indicates the type of virtual keyboard used but doesn’t enforce
input since mobile OS’s don’t do that.
7. Label price = new Label(Restaurant.getInstance().formatCurrency(d.price.get()),
"PriceBadge");
d.price.addChangeListener(pl ->
price.setText(Restaurant.getInstance().formatCurrency(d.price.get())));
Label title = new Label(d.name.get(), “DishName");
d.name.addChangeListener(pl -> title.setText(d.name.get()));
Label description = new Label(d.description.get(), "DishDescription");
d.description.addChangeListener(pl -> description.setText(d.description.get()));
Container titleAndDescription = BoxLayout.encloseY(title, description);
titleAndDescription.setUIID("BlackGradient");
Container cnt = LayeredLayout.encloseIn(sb,
BorderLayout.south(titleAndDescription),
FlowLayout.encloseRight(price)
);
DishListForm
UIID
Foreground white
background 3f51b5
Transparency 200
Alignment right
padding 1mm
margin 0
Margin top 4mm
Next we’ll handle prices on dishes, as you can see in this code we can just add a price label with the right price badge UIID. We use a property listener to automatically
update the price as it changes in the underlying object
We align the price to the right using the flow layout, if we’d have used just text alignment the purple background would have stretched thru the entire width of the space.
The price badge UIID has most of the settings you would expect for color alignment and padding and even a bit of translucency. The thing that pushes it from the top is a
4mm margin on the top side which places it at “just the right place”.
8. public class DetailsForm extends BaseNavigationForm {
private Builder bld = new Builder();
public DetailsForm(AppSettings app) {
super(app, BoxLayout.y());
TextField emailField = addTextAndLabel(
"E-mail - will receive purchases from the generated app", "", TextField.EMAILADDR);
emailField.addActionListener(e -> {
app.restaurantEmail.set(emailField.getText());
AppStorage.getInstance().update(app);
bld.updateRestaurantSettings(app);
});
TextField urlField = addTextAndLabel("Website URL", "", TextField.URL);
urlField.addActionListener(e -> {
Restaurant.getInstance().website.set(urlField.getText());
bld.updateRestaurantSettings(app);
});
MultiButton address = new MultiButton("Address & Location");
if(Restaurant.getInstance().navigationAddress.get() != null &&
Restaurant.getInstance().navigationAddress.get().length() > 0) {
address.setTextLine2(Restaurant.getInstance().navigationAddress.get());
} else {
address.setTextLine2("...");
}
add(address);
address.addActionListener(e -> new AddressForm().show());
DetailsForm
Next we’ll address the details form which is mostly pretty trivial lets go over it step by step
9. public class DetailsForm extends BaseNavigationForm {
private Builder bld = new Builder();
public DetailsForm(AppSettings app) {
super(app, BoxLayout.y());
TextField emailField = addTextAndLabel(
"E-mail - will receive purchases from the generated app", "", TextField.EMAILADDR);
emailField.addActionListener(e -> {
app.restaurantEmail.set(emailField.getText());
AppStorage.getInstance().update(app);
bld.updateRestaurantSettings(app);
});
TextField urlField = addTextAndLabel("Website URL", "", TextField.URL);
urlField.addActionListener(e -> {
Restaurant.getInstance().website.set(urlField.getText());
bld.updateRestaurantSettings(app);
});
MultiButton address = new MultiButton("Address & Location");
if(Restaurant.getInstance().navigationAddress.get() != null &&
Restaurant.getInstance().navigationAddress.get().length() > 0) {
address.setTextLine2(Restaurant.getInstance().navigationAddress.get());
} else {
address.setTextLine2("...");
}
add(address);
address.addActionListener(e -> new AddressForm().show());
DetailsForm
I’ve enclosed common code for text field and label creation in the addTextAndLabel method. This ended up reducing only a small part of the clutter and wasn’t as
worthwhile as I had initially hoped
10. public class DetailsForm extends BaseNavigationForm {
private Builder bld = new Builder();
public DetailsForm(AppSettings app) {
super(app, BoxLayout.y());
TextField emailField = addTextAndLabel(
"E-mail - will receive purchases from the generated app", "", TextField.EMAILADDR);
emailField.addActionListener(e -> {
app.restaurantEmail.set(emailField.getText());
AppStorage.getInstance().update(app);
bld.updateRestaurantSettings(app);
});
TextField urlField = addTextAndLabel("Website URL", "", TextField.URL);
urlField.addActionListener(e -> {
Restaurant.getInstance().website.set(urlField.getText());
bld.updateRestaurantSettings(app);
});
MultiButton address = new MultiButton("Address & Location");
if(Restaurant.getInstance().navigationAddress.get() != null &&
Restaurant.getInstance().navigationAddress.get().length() > 0) {
address.setTextLine2(Restaurant.getInstance().navigationAddress.get());
} else {
address.setTextLine2("...");
}
add(address);
address.addActionListener(e -> new AddressForm().show());
DetailsForm
Address, colors and other elements are represented by a button which will navigate to a deeper “editor” form. This is important as it allows us to keep the UI in this form
relatively simple without too much clutter.
I considered using something like an accordion UI but using navigation was simpler.
11. MultiButton styles = new MultiButton("Colors & Fonts");
styles.setTextLine2("...");
add(styles);
styles.addActionListener(e -> new StyleForm().show());
MultiButton about = new MultiButton("About Page");
styles.setTextLine2("...");
add(about);
styles.addActionListener(e -> new AboutRestaurantForm().show());
}
private TextField addTextAndLabel(String label, String value) {
TextField tf = new TextField(value);
tf.setHint(label);
add(new Label(label, "TextFieldLabel")).
add(tf);
return tf;
}
DetailsForm
As I mentioned before addTextFieldAndLabel is relatively simple. It sets the hint and just adds the components into place