Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add offerbook domain #72

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bisqapps/androidNode/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ dependencies {
implementation(libs.bisq.core.application)
implementation(libs.bisq.core.chat)
implementation(libs.bisq.core.presentation)
implementation(libs.bisq.core.bisq.easy)

// protobuf
implementation(libs.protobuf.gradle.plugin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.core.util.Supplier
import bisq.account.AccountService
import bisq.application.ApplicationService
import bisq.application.State
import bisq.bisq_easy.BisqEasyService
import bisq.bonded_roles.BondedRolesService
import bisq.bonded_roles.security_manager.alert.AlertNotificationsService
import bisq.chat.ChatService
Expand All @@ -38,7 +39,6 @@ import bisq.settings.SettingsService
import bisq.support.SupportService
import bisq.trade.TradeService
import bisq.user.UserService
import com.google.common.base.Preconditions
import lombok.Getter
import lombok.Setter
import lombok.extern.slf4j.Slf4j
Expand Down Expand Up @@ -87,6 +87,8 @@ class AndroidApplicationService(androidMemoryReportService: AndroidMemoryReportS
Supplier { applicationService.chatService }
var settingsServiceSupplier: androidx.core.util.Supplier<SettingsService> =
Supplier { applicationService.settingsService }
var bisqEasyServiceSupplier: androidx.core.util.Supplier<BisqEasyService> =
Supplier { applicationService.bisqEasyService }
var supportServiceSupplier: androidx.core.util.Supplier<SupportService> =
Supplier { applicationService.supportService }
var systemNotificationServiceSupplier: androidx.core.util.Supplier<SystemNotificationService> =
Expand All @@ -107,7 +109,6 @@ class AndroidApplicationService(androidMemoryReportService: AndroidMemoryReportS
val log: Logger = LoggerFactory.getLogger(ApplicationService::class.java)
}

val state = Observable(State.INITIALIZE_APP)
private val shutDownErrorMessage = Observable<String>()
private val startupErrorMessage = Observable<String>()

Expand Down Expand Up @@ -150,11 +151,11 @@ class AndroidApplicationService(androidMemoryReportService: AndroidMemoryReportS
val supportService: SupportService
val systemNotificationService = SystemNotificationService(Optional.empty())
val tradeService: TradeService
val bisqEasyService:BisqEasyService
val alertNotificationsService: AlertNotificationsService
val favouriteMarketsService: FavouriteMarketsService
val dontShowAgainService: DontShowAgainService


init {
chatService = ChatService(
persistenceService,
Expand Down Expand Up @@ -186,6 +187,20 @@ class AndroidApplicationService(androidMemoryReportService: AndroidMemoryReportS
settingsService
)

bisqEasyService = BisqEasyService( persistenceService,
securityService,
networkService,
identityService,
bondedRolesService,
accountService,
offerService,
contractService,
userService,
chatService,
settingsService,
supportService,
systemNotificationService,
tradeService)

alertNotificationsService =
AlertNotificationsService(settingsService, bondedRolesService.alertService)
Expand Down Expand Up @@ -342,15 +357,6 @@ class AndroidApplicationService(androidMemoryReportService: AndroidMemoryReportS
}
}

private fun setState(newState: State) {
Preconditions.checkArgument(
state.get().ordinal < newState.ordinal,
"New state %s must have a higher ordinal as the current state %s", newState, state.get()
)
state.set(newState)
log.info("New state {}", newState)
}

private fun logError(throwable: Throwable): Boolean {
log.error("Exception at shutdown", throwable)
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package network.bisq.mobile.android.node.di

import network.bisq.mobile.android.node.AndroidApplicationService
import network.bisq.mobile.android.node.domain.bootstrap.NodeApplicationBootstrapFacade
import network.bisq.mobile.android.node.domain.data.repository.NodeGreetingRepository
import network.bisq.mobile.android.node.domain.offerbook.NodeOfferbookServiceFacade
import network.bisq.mobile.android.node.domain.user_profile.NodeUserProfileServiceFacade
import network.bisq.mobile.android.node.presentation.NodeMainPresenter
import network.bisq.mobile.android.node.service.AndroidMemoryReportService
import network.bisq.mobile.domain.data.repository.main.bootstrap.ApplicationBootstrapFacade
import network.bisq.mobile.domain.offerbook.OfferbookServiceFacade
import network.bisq.mobile.domain.user_profile.UserProfileServiceFacade
import network.bisq.mobile.presentation.MainPresenter
import network.bisq.mobile.presentation.ui.AppPresenter
Expand All @@ -15,9 +16,6 @@ import org.koin.dsl.bind
import org.koin.dsl.module

val androidNodeModule = module {
// this one is for example properties, will be eliminated soon
single<NodeGreetingRepository> { NodeGreetingRepository() }

single<AndroidMemoryReportService> {
AndroidMemoryReportService(androidContext())
}
Expand All @@ -28,8 +26,8 @@ val androidNodeModule = module {

single<UserProfileServiceFacade> { NodeUserProfileServiceFacade(get()) }


single<OfferbookServiceFacade> { NodeOfferbookServiceFacade(get()) }
// this line showcases both, the possibility to change behaviour of the app by changing one definition
// and binding the same obj to 2 different abstractions
single<MainPresenter> { NodeMainPresenter(get(), get(), get()) } bind AppPresenter::class
single<MainPresenter> { NodeMainPresenter(get(), get(), get(), get()) } bind AppPresenter::class
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package network.bisq.mobile.android.node.domain.offerbook

import bisq.common.currency.Market
import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.StateFlow
import network.bisq.mobile.android.node.AndroidApplicationService
import network.bisq.mobile.android.node.domain.offerbook.market.MarketChannelSelectionService
import network.bisq.mobile.android.node.domain.offerbook.market.MarketListItemService
import network.bisq.mobile.android.node.domain.offerbook.offers.OfferbookListItemService
import network.bisq.mobile.client.replicated_model.common.currency.MarketListItem
import network.bisq.mobile.domain.offerbook.OfferbookListItem
import network.bisq.mobile.domain.offerbook.OfferbookMarket
import network.bisq.mobile.domain.offerbook.OfferbookServiceFacade

class NodeOfferbookServiceFacade(private val applicationServiceSupplier: AndroidApplicationService.Supplier) :
OfferbookServiceFacade {

// Dependencies


// Properties
override val marketListItemList: List<MarketListItem> get() = marketListItemService.marketListItems
override val offerbookListItemList: StateFlow<List<OfferbookListItem>> get() = offerbookListItemService.offerbookListItems
override val selectedOfferbookMarket: StateFlow<OfferbookMarket> get() = marketChannelSelectionService.selectedOfferbookMarket

// Misc
private val log = Logger.withTag(this::class.simpleName ?: "NodeOfferbookServiceFacade")
private var offerbookListItemService: OfferbookListItemService =
OfferbookListItemService(applicationServiceSupplier)
private var marketListItemService: MarketListItemService =
MarketListItemService(applicationServiceSupplier)
private var marketChannelSelectionService: MarketChannelSelectionService =
MarketChannelSelectionService(applicationServiceSupplier)

// Life cycle
override fun initialize() {
marketListItemService.initialize()
marketChannelSelectionService.initialize()
offerbookListItemService.initialize()
}

override fun resume() {
marketListItemService.resume()
marketChannelSelectionService.resume()
offerbookListItemService.resume()
}

override fun dispose() {
marketListItemService.dispose()
marketChannelSelectionService.dispose()
offerbookListItemService.dispose()
}

// API
override fun selectMarket(marketListItem: MarketListItem) {
val market = Market(
marketListItem.baseCurrencyCode,
marketListItem.quoteCurrencyCode,
marketListItem.baseCurrencyName, marketListItem.quoteCurrencyName
)
marketChannelSelectionService.selectMarket(market)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package network.bisq.mobile.android.node.domain.offerbook.market

import bisq.bonded_roles.market_price.MarketPriceService
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookSelectionService
import bisq.common.currency.Market
import bisq.common.observable.Pin
import bisq.presentation.formatters.PriceFormatter
import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import network.bisq.mobile.android.node.AndroidApplicationService
import network.bisq.mobile.domain.LifeCycleAware
import network.bisq.mobile.domain.offerbook.OfferbookMarket


class MarketChannelSelectionService(private val applicationServiceSupplier: AndroidApplicationService.Supplier) :
LifeCycleAware {

// Dependencies
private lateinit var bisqEasyOfferbookChannelService: BisqEasyOfferbookChannelService
private lateinit var bisqEasyOfferbookChannelSelectionService: BisqEasyOfferbookSelectionService
private lateinit var marketPriceService: MarketPriceService

// Properties
private val _selectedOfferbookMarket = MutableStateFlow(OfferbookMarket("", "", "", ""))
val selectedOfferbookMarket: StateFlow<OfferbookMarket> get() = _selectedOfferbookMarket

// Misc
private val log = Logger.withTag(this::class.simpleName ?: "SelectedMarket")

private var selectedChannelPin: Pin? = null

// Life cycle
override fun initialize() {
bisqEasyOfferbookChannelService =
applicationServiceSupplier.chatServiceSupplier.get().bisqEasyOfferbookChannelService
bisqEasyOfferbookChannelSelectionService =
applicationServiceSupplier.chatServiceSupplier.get().bisqEasyOfferbookChannelSelectionService
marketPriceService =
applicationServiceSupplier.bondedRolesServiceSupplier.get().marketPriceService

observeSelectedChannel()
}

override fun resume() {
observeSelectedChannel()
}

override fun dispose() {
selectedChannelPin?.unbind()
selectedChannelPin = null
}

// API
fun selectMarket(market: Market) {
log.i { "selectMarket " + market }
bisqEasyOfferbookChannelService.findChannel(market).ifPresent {
bisqEasyOfferbookChannelSelectionService.selectChannel(it)
}
}

// Private
private fun observeSelectedChannel() {
selectedChannelPin =
bisqEasyOfferbookChannelSelectionService.selectedChannel.addObserver { marketChannel ->
marketChannel as BisqEasyOfferbookChannel
val market = marketChannel.market

marketPriceService.setSelectedMarket(market)

val title = marketChannel.shortDescription
val iconId = "channels-" + marketChannel.id.replace(".", "-")
val marketCodes = market.marketCodes
val formattedPrice = marketPriceService.findMarketPrice(market)
.map { PriceFormatter.format(it.priceQuote, true) }
.orElse("")

_selectedOfferbookMarket.value =
OfferbookMarket(title, iconId, marketCodes, formattedPrice)

log.i { "selectedChannel " + marketChannel }
log.i { "title " + title }
log.i { "iconId " + iconId }
log.i { "_marketCodes " + marketCodes }
log.i { "_formattedPrice " + formattedPrice }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package network.bisq.mobile.android.node.domain.offerbook.market

import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage
import bisq.common.observable.Pin
import co.touchlab.kermit.Logger
import network.bisq.mobile.android.node.AndroidApplicationService
import network.bisq.mobile.client.replicated_model.common.currency.MarketListItem
import network.bisq.mobile.domain.LifeCycleAware


class MarketListItemService(private val applicationServiceSupplier: AndroidApplicationService.Supplier) :
LifeCycleAware {
// Properties
private val _marketListItems: List<MarketListItem> by lazy { fillMarketListItems() }
val marketListItems: List<MarketListItem> get() = _marketListItems

// Misc
private val log = Logger.withTag(this::class.simpleName ?: "Markets")
private var numOffersObservers: MutableList<NumOffersObserver> = mutableListOf()

// Life cycle
override fun initialize() {
}

override fun resume() {
numOffersObservers.forEach { it.resume() }
}

override fun dispose() {
numOffersObservers.forEach { it.dispose() }
}

private fun fillMarketListItems(): MutableList<MarketListItem> {
val marketListItems: MutableList<MarketListItem> = mutableListOf()
applicationServiceSupplier.chatServiceSupplier.get().bisqEasyOfferbookChannelService.channels
.forEach { channel ->
// We convert channel.market to our replicated Market model
val marketListItem = MarketListItem(
channel.market.baseCurrencyCode,
channel.market.quoteCurrencyCode,
channel.market.baseCurrencyName,
channel.market.quoteCurrencyName,
)
marketListItems.add(marketListItem)

val numOffersObserver = NumOffersObserver(channel, marketListItem::setNumOffers)
numOffersObservers.add(numOffersObserver)
}
return marketListItems
}

// Inner class
inner class NumOffersObserver(
private val channel: BisqEasyOfferbookChannel,
val setNumOffers: (Int) -> Unit
) {
private var channelPin: Pin? = null

init {
channelPin = channel.chatMessages.addObserver { this.updateNumOffers() }
}

fun resume() {
dispose()
channelPin = channel.chatMessages.addObserver { this.updateNumOffers() }
}

fun dispose() {
channelPin?.unbind()
channelPin = null
}

private fun updateNumOffers() {
val numOffers = channel.chatMessages.stream()
.filter { obj: BisqEasyOfferbookMessage -> obj.hasBisqEasyOffer() }
.count().toInt()
setNumOffers(numOffers)
}
}
}
Loading
Loading