Skip to main content

Neds Toolbox

Overview

The Toolbox is a feature specific to Neds, accessible to users by clicking on the Toolbox item in the AppBar. This feature provides users with a wide variety of tools designed to enhance their betting experience. Each tool is presented as an item in the Toolbox, featuring an icon, title, brief description, and the current number of allocations available for that tool type. The Toolbox items can be categorized into Product Items and Support items.

These are the Product items available on the Toolbox:

  • Price Boots
  • No Place
  • Multi Rescue
  • Take Down
  • Quaddie Rescue
  • Extra Nudge
  • Bet Back
  • Back Up
  • Fluc Up
  • Trifecta Rescue
  • Same Game Multi Cover

Aditionally, these are the Support Items available on the Toolbox:

  • Punter Assist
  • Toolbox Tips

When users click on a Product item, they will be navigated to a detail page that provides a description of the selected tool. This page also includes a "Read More" button, which will display additional information in a web view. For the Support Items, selecting the Punter Assist option navigates users to the `"AccountPunterAssist" screen. As for the Toolbox Tips option, it will open an external browser directing users to the toolbox tips website for further guidance and information.

Toolbox

Android Toolbox

The Toolbox was implemented using the Model-View-ViewModel (MVVM) architecture in conjunction with the Clean Architecture framework.

The entry point to the Toolbox feature is the ToolboxEntryScreen component (located in presentation/ToolboxEntryScreen.kt in the Neds flavour). This component calls the ToolboxList component (located in the presentation/list/ToolboxList.kt file in main) and passes through a list of products ToolboxProductItem, a list of support items ToolboxSupportItem, and the events required for the Toolbox.

In the components folder, we find the ToolboxItemCard.kt used to display each item in the Toolbox. The models folder serves to define the data structures directly used in the presentation layer, these models hold the data for which the UI will display. To highlight a few, ToolboxModels.kt includes various item definitions such as ToolboxProductItem and ToolboxSupportItem, used by the UI components to render each item. Another essential data class to mention is the ToolboxState, which encapsulates the overall state of the Toolbox, managing the collection of product items and other important states.

presentation/list/utils/ToolboxListTypeExtensions.kt

This file contains several extension functions for the ToolboxProductType enum, providing additional behaviors and properties relevant to the functionality of the Toolbox.

Some noteworthy functions to mention include:

  • getProductGradientColour(), which returns a specific gradient brush for different types of products.
  • getProductHeading() and getProductSubheading(), which provide the display labels and descriptions for the UI components.
  • getProductLeadingImageId() and getLeadingIconBackgroundColor(), provide the drawable resource IDs and background colors for the icons associated with each product type.

presentation/list/models/ToolboxSupportType.kt

This class defines an enum type ToolboxSupportType to categorize PUNTER_ASSIST and TOOLBOX_TIPS, used to define the itemType in ToolboxSupportItem. It also contains several extension functions relevant to the Toolbox support items.

Some noteworthy functions to mention include:

  • getSupportItemHeading() and getSupportItemSubheading(), which return resource IDs for headings and subheading texts.
  • getLeadingIconId() and getLeadingIconBackgroundColor(), which provide IDs for the images and background colors for the icons associated with each support item.

ProductDetailViewModel.kt

The ProductDetailEntryScreen is responsible for displaying the details of the product selected from the ToolBox screen. It takes the ProductDetailViewModel as a parameter and manages the state related to the product details.

ToolboxListViewModel.kt

The ToolboxList.kt component is responsible for rendering the main components in the ToolBox, including all the ToolBoxProducts and the ToolBoxSupportItems. The ToolboxListViewModel manages the state and business logic related to Toolbox features.

It has two dependencies: LoadToolboxInfoUseCase and GetToolboxTipsUrlUseCase. Upon initialization, the functions observeToolboxBalance() and getToolboxTipsUrl() are called, which invoke the LoadToolboxInfoUseCase() and GetToolboxTipsUrlUseCase() functions, respectively.

The LoadToolboxInfoUseCase updates the ToolBoxInfo to the state of the ViewModel, which is then used to map the balance of each product and display a badge count for each product in the Toolbox.

The GetToolboxTipsUrlUseCase retrieves the URL of the Toolbox Tips. This URL is obtained using the AppConfigProvider.

Finally, the navigateToProductDetail function will be responsible for navigating to the product detail screen for a specified ToolboxProductType:

toolbox/presentation/list/ToolboxViewModel.kt
fun navigateToProductDetail(productType: ToolboxProductType) {
val productTypeArgument = productType.name
navController.navigate(
route = ModalRoute.ToolboxProductDetail.routeWithParams(
productType = productTypeArgument,
),
navigationType = EntainNavController.NavigationType.PushIfNotOnTop(),
)
}

Navigation will be done by using navController.navigate function, and the route specified is ModalRoute.ToolboxProductDetail.routeWithParams with productType as parameter which will contain the selected ToolBox product.

ToolboxList.kt & ToolboxProductDetailEntryScreen.kt

These entry screens serve as the primary user interfaces for interacting with the Toolbox feature. The ToolboxList is used as the main Toolbox screen and will display various toolbox products and support items available to the user. This file contains the main composables used to draw the ToolBox feature. Specifically, the Toolbox component will be in charge of managing the Toolbox Top Bar with the gradient background and scroll behavior, the Header Image to resize dynamically, the Toolbox Product and Support items, and also the Close button.

As for the ToolboxProductDetailEntryScreen, it will be used as the entry screen to display detailed information about the selected toolbox product. The ToolboxProductDetail contains the main composables used to draw the details of the product selected from the ToolBox main screen. It will be in charge of managing the Top Bar to use a gradient background and back navigation, the Header section which will display the product image and description within the curved container, product description along with a "Learn More" button.

domain/list/model/ToolboxInfo.kt

This class defines a data class, ToolboxInfo, representing various toolbox products and their associated balances. Each product, such as "priceBoost" and "betBack", is represented by a ToolboxProduct data class, containing its balances.

This class encapsulates the data structure for ToolboxInfo, which encompasses a collection of toolbox products along with their corresponding balances. Each product, such as "priceBoost" and "betBack", is represented as a ToolboxProduct data class, which includes information about its balances.

The ToolboxInfo class also includes a balance property, which calculates the total balance across all toolbox products.

domain/list/model/ToolboxInfo.kt
data class ToolboxInfo(
val priceBoost: ToolboxProduct = ToolboxProduct(),
val betBack: ToolboxProduct = ToolboxProduct(),
val multiRescue: ToolboxProduct = ToolboxProduct(),
val sameGameMulti: ToolboxProduct = ToolboxProduct(),
val takeDown: ToolboxProduct = ToolboxProduct(),
val noPlace: ToolboxProduct = ToolboxProduct(),
val backUp: ToolboxProduct = ToolboxProduct(),
val extraNudge: ToolboxProduct = ToolboxProduct(),
val trifectaRescue: ToolboxProduct = ToolboxProduct(),
val flucUp: ToolboxProduct = ToolboxProduct(),
val quaddieCover: ToolboxProduct = ToolboxProduct(),
) {
val balance: Int
get() = priceBoost.balance + betBack.balance + multiRescue.balance + sameGameMulti.balance +
takeDown.balance + noPlace.balance + backUp.balance + extraNudge.balance +
trifectaRescue.balance + flucUp.balance + quaddieCover.balance
}
domain/list/model/ToolboxInfo.kt
data class ToolboxProduct(
val balance: Int = 0,
val productOffers: List<ProductOffer> = emptyList(),
)

The ToolboxProduct's can contain a list ProductOffer's which are displayed in the ToolBoxProductDetail component. A list of ToolboxItemCard's will be displayed below the ProductDescription.

ToolBox

ToolboxDataMapper.kt

This mapper is used by the LoadToolboxInfoUseCase. The map function takes an instance of ToolboxBridgeData (fetched from the SharedStateGetUseCase<ToolboxBridgeData>) and maps it to a corresponding ToolboxInfo object. Within this function, the attributes of the ToolboxInfo object are populated based on the data from ToolboxBridgeData. Each ToolboxProduct within the ToolboxInfo is assigned the corresponding balances and product offers based on the data from ToolboxBridgeData.

Considerations when adding a new brand - Toolbox

While the Toolbox List is exclusively used in Neds, the code lives in the main folder of the code which allows for easy reusability. The ToolboxEntryScreen lives in each brand flavour folder which allows the application of the brand theme. For a new brand to use the Toolbox List, the new brand must add its own ToolboxEntryScreen calling the ToolboxList component, applying its own brand theme, and passing on the state and necessary event handlers. The existing ToolboxListViewModel can be injected or a new View Model can be created with specific functionality.

iOS Toolbox

Architecture

On iOS, the Neds Toolbox is implemented using the Combine architecture and follows the VENOM pattern, consisting of a view, viewModel, presenter, transformer, and interactor.

The main UI components and view logic for Toolbox, can be found under the GenerosityUI in the PresentationLayer, and the Toolbox module in the DomainLayer.

The path into the GenerosityUI is via the GenerosityCoordinator. Used by both Neds Toolbox and Ladbrokes Locker. The method getViewController() checks which app target is running and will contruct the Toolbox view using makeToolboxViewController() if the target is Neds.

Similarly the ToolboxProductCoordintaor is the entry point for the Toolbox Product details page, this is pushed onto the stack when a Toolbox Product is selected from the menu.

The GenerosityCoordinator conforms to ToolboxPresenterDelegate which handles navigation into the native Toolbox Product details view. There is also two additional navigation points, Punter Assist which deeplinks to the help section in the More Menu and Toolbox Tips which opens a webpage outside of the app.

The ToolboxProductCoordintaor conforms to the ToolboxProductPresenterDelegate. There are two main navigation points for the Toolbox Product details screen, Firsty there is Learn More, found in the Toolbox Product description at the top of the page. When this is selected, a Webview will be displayed modally, containing details about that Toolbox Product. At the bottom of the page you will find the Toolbox Product Allocations list, the routes for these items are determined by the type of allocation associated with that Toolbox Product. When selected, they will navigate to the relavent section of the Neds app.

Toolbox Views

Two main screens make up the Neds Toolbox, ToolboxView is the first screen presented, it contains a list of all the Toolbox Product types available to the user. Once a Toolbox Product has been selected, the user will be pushed to the ToolboxProductView, containing information about the product and the allocations available.

Both of these screens use the ToolboxScrollView, this custom scrollview sends updates on its scroll position using the callback method onScrollPositionUpdated. The updated values are received by the ToolboxHeaderView animating the provided logo, updating the header size and shape.

ToolboxPresenter

The ToolboxPresenter, receives balance data related to all the Toolbox Products, from the ToolboxBalanceInteractor, in addition it also receives the url used for the Toolbox Tips route, from the AppConfigInteractor.

GenerosityUI/NedsToolboxUI/ToolboxPresenter.swift

func startObservers() {
// Toolbox Balance
toolboxBalanceInteractor
.toolboxBalancePublisher()
.receive(on: DispatchQueue.main)
.sink { [unowned self] balance in
self.updateState(balance: balance)
Task { await self.updateAllBalancesUI() }
}
.store(in: &cancellables)

// Toolbox tips url
appConfigInteractor
.toolboxTipsModelPublisher()
.receive(on: DispatchQueue.main)
.sink { [unowned self] data in
self.updateToolboxTipsURLWithData(data)
Task { await self.updateUI() }
}
.store(in: &cancellables)
}

ToolboxTransformer / ToolboxItemBuilder

When updated data is received from the ToolboxBalanceInteractor, its then passed to the ToolboxTransformer whose child dependancy ToolboxItemBuilder, converts the data into a ToolboxItemViewModel.

GenerosityUI/NedsToolboxUI/ToolboxTransormer.swift

func makeMenuItemViewModels(with state: ToolboxPresenter.State) -> [ToolboxItemViewModel] {
ToolboxProduct.allCases.map {
toolboxItemViewBuilder.makeMenuItem(
product: $0,
toolboxBalance: state.toolboxBalance,
viewType: .menu
)
}
}

func makeProductAllocationItemViewModels(
with state: ToolboxPresenter.State,
product: ToolboxProduct
) -> [ToolboxItemViewModel] {
toolboxItemViewBuilder.makeProductAllocationItems(
product: product,
toolboxBalance: state.toolboxBalance,
viewType: .productDetails
)
}

This ToolboxItemViewModel serves as a display for both the Menu Items and the Product Allocation Items on the details screen. An individual menu item, displays a combined balance of all allocations for the given product. Selecting that product, will move the user to the details screen, displaying the individual product allocation items and their balance.

ToolBox

ToolboxProduct

ToolboxProduct is a enumerator which contains each product type as listed in the Toolbox menu.

GenerosityUI/NedsToolboxUI/Model/ToolboxProduct.swift

public enum ToolboxProduct: String, CaseIterable {
case priceBoost
case backUp
case extraNudge
case betBack
case flucUp
case multiRescue
case noPlace
case quaddieRescue
case takeDown
case trifectaRescue
case sgmCover
case punterAssist
case toolboxTips
}

This class also contains computed properties representing the Toolbox Product items gradient header colour, menu icon, icon colour, logo and description.


public extension ToolboxProduct {
var logo: EntainIcon {
switch self {
case .priceBoost: return EntainIcon.priceBoostLogo
case .extraNudge: return EntainIcon.extraNudgeLogo
case .multiRescue: return EntainIcon.multiRescueLogo
case .noPlace: return EntainIcon.noPlaceLogo
case .backUp: return EntainIcon.backUpLogo
case .betBack: return EntainIcon.betBackLogo
case .quaddieRescue: return EntainIcon.quaddieRescueLogo
case .trifectaRescue: return EntainIcon.trifectaRescueLogo
case .flucUp: return EntainIcon.flucUpLogo
case .takeDown: return EntainIcon.takeDownLogo
case .sgmCover: return EntainIcon.sgmCoverLogo
case .punterAssist, .toolboxTips: return EntainIcon.toolboxLogo
}
}
}

// Gradient Colours
public extension ToolboxProduct {
var startColor: Color {
switch self {
case .priceBoost:
return ToolboxTheme.blue.startColor

case .noPlace, .backUp, .betBack, .quaddieRescue, .extraNudge, .trifectaRescue, .flucUp:
return ToolboxTheme.purple.startColor

case .sgmCover, .multiRescue, .takeDown:
return ToolboxTheme.teal.startColor

case .punterAssist, .toolboxTips:
return ToolboxTheme.orange.startColor
}
}
}

ToolboxTheme

ToolboxTheme is a enumerator which contains colour variations for the various Toolbox Product types, these colours are displayed in the ToolboxHeaderView as a gradient and as an icon colour for the menu items.

GenerosityUI/NedsToolboxUI/Theme/ToolboxTheme.swift

public enum ToolboxTheme: Equatable {
case orange
case blue
case purple
case teal

var startColor: Color {
switch self {
case .orange: return EntainColor.brandGradientEnd.color
case .blue: return Self.blueLight
case .purple: return Self.purpleLight
case .teal: return Self.tealLight
}
}

var endColor: Color {
switch self {
case .orange: return EntainColor.brandGradientBegin.color
case .blue: return Self.blueDark
case .purple: return Self.purpleDark
case .teal: return Self.tealDark
}
}
}

Android Troubleshooting

Static Resources from the "static-ui" Project

When managing static resources across different brands, the preferred approach is to use resources from the "static-ui" project. This repository has a comprehensive collection of static images, files, and assets used in all of the brands under Entain.

To access these resources, clone the repository and follow the setup instructions provided. For more details, visit the following link: https://git.neds.sh/technology/code/ui/static

iOS Troubleshooting

During development, the FullscreenUIHostingController was built for the UserTrayView and the LockerView. This is due to the view not resizing correctly after its intrinsic content size changes. This is most likely due to the constraints not being updated when the size changes.

This controller ensures that the view's bounds match the superview’s bounds.

Custom Modal Animation

The Locker and the User Tray have unique modal animations. As these views are hosted by UIKit, it made sense to create UIKit present and dismiss animations. The SlideInPresentationManager is the delegate that contains the animation and the controller for these animations.

The SlideInPresentationController contains the dimensions and the background dimming view. The SlideInPresentationAnimator contains the animations for the sliding down.

This SlideInPresentationManager has a property called direction that dictates the slide in direction. It defaults to the top to align with the functionality of the User Tray and the Locker.

Note: The Neds Toolbox does not use this delegate as its modal presentation uses the system’s default.

Configuration

  • toolbox.toolbox-blog-tips-url - Blog and Tips URL - from App Config.

Resources

RoleContact
PMTBC
Android LeadAnthony Librio
iOS LeadNicholas Vella