Skip to content

Commit

Permalink
UI iteration 2 (#73)
Browse files Browse the repository at this point in the history
* My Trades UI 1/3

* Buy/Sell UI

 - Dialog component

 - Exchange screen improvements

 - Offerbook UI - Basic

* - CurrencyRepository; Dialog component; Improved CurrencyListScreen and OfferListScreen

* - minor fixes: coil3 in .toml; Misc

* - minor cleanups - removed dead code, updated TODO comments

* - added translation keys/strings to CurrencyList/OfferList screens

* - added translation keys/strings to CurrencyList/OfferList screens (missed staging the files)

* - replace attach call with custom composable RememberPresenterLifecycle which handle both attach and unattach

---------

Co-authored-by: Rodrigo Varela <[email protected]>
  • Loading branch information
nostrbuddha and rodvar authored Nov 26, 2024
1 parent e22db41 commit 3c8ce93
Show file tree
Hide file tree
Showing 63 changed files with 987 additions and 161 deletions.
2 changes: 2 additions & 0 deletions bisqapps/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ androidx-lifecycle = "2.8.2"
androidx-test-compose-ver = "1.6.8"
androidx-multidex = "2.0.1"
bisq-core = "2.1.2"
coilCompose = "3.0.3"
compose-plugin = "1.7.0"
junit = "4.13.2"
kotlinxDatetime = "0.4.0"
Expand Down Expand Up @@ -64,6 +65,7 @@ lombok-lib = { strictly = '1.18.34' }
typesafe-config-lib = { strictly = '1.4.3' }

[libraries]
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit-v180 = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinTestJunit" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package network.bisq.mobile.domain.data.model

data class FiatCurrency(
val flagImage: String,
val name: String,
val code: String,
val offerCount: Number
)

class Currencies(val currencies: List<FiatCurrency> = listOf()): BaseModel()

interface CurrenciesFactory {
fun createCurrencies(): Currencies
}

class DefaultCurrenciesFactory : CurrenciesFactory {
override fun createCurrencies() = Currencies()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package network.bisq.mobile.domain.data.model

// TODO: Update later based on model class from bisq2 lib
data class BisqOffer (
val id: String = "offer_283UANJD19A",
val isBuy: Boolean = true,
val amIMaker: Boolean = false,
val price: Number = 97000,
val currency: String = "USD",
val fiatAmount: Number = 1000, //Should be a range
val satsAmount: Number= 1030927, //Should be a range

val partyName: String = "satoshi",
val partyRatings: Double = 4.2,
val partyDP: String = "", // Image URL
)

class MyTrades(val trades: List<BisqOffer> = listOf()): BaseModel()

interface MyTradesFactory {
fun createMyTrades(): MyTrades
}

class DefaultMyTradesFactory : MyTradesFactory {
override fun createMyTrades() = MyTrades()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package network.bisq.mobile.domain.data.repository

import kotlinx.coroutines.runBlocking
import network.bisq.mobile.domain.data.model.Currencies
import network.bisq.mobile.domain.data.model.FiatCurrency

// TODO:
// androidNode will populate List<FiatCurrency> from bisq2 libs
// xClients will populate List<FiatCurrency> via API
open class CurrenciesRepository : SingleObjectRepository<Currencies>() {
init {
runBlocking {
val currencies = Currencies(
currencies = listOf(
FiatCurrency(
flagImage = "currency_aed.png",
name = "United Arab Emirates Dirham",
code = "aed",
offerCount = 9
),
FiatCurrency(flagImage = "currency_ars.png", name = "Argentine Peso", code = "ars", offerCount = 0),
FiatCurrency(
flagImage = "currency_aud.png",
name = "Australian Dollar",
code = "aud",
offerCount = 12
),
FiatCurrency(flagImage = "currency_eur.png", name = "Euro", code = "eur", offerCount = 66),
FiatCurrency(
flagImage = "currency_gbp.png",
name = "British Pound Sterling",
code = "gbp",
offerCount = 3
),

FiatCurrency(flagImage = "currency_jpy.png", name = "Japanese Yen", code = "jpy", offerCount = 2),

FiatCurrency(flagImage = "currency_qar.png", name = "Qatari Rial", code = "qar", offerCount = 4),
FiatCurrency(flagImage = "currency_sek.png", name = "Swedish Krona", code = "sek", offerCount = 18),
FiatCurrency(
flagImage = "currency_sgd.png",
name = "Singapore Dollar",
code = "sgd",
offerCount = 16
),
FiatCurrency(flagImage = "currency_usd.png", name = "US Dollar", code = "usd", offerCount = 62),
)
)
create(currencies)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
package network.bisq.mobile.domain.data.repository

import network.bisq.mobile.domain.data.model.BisqStats
import network.bisq.mobile.domain.data.model.BtcPrice
import network.bisq.mobile.domain.data.model.Greeting
import network.bisq.mobile.domain.data.model.Settings
import kotlinx.coroutines.runBlocking
import network.bisq.mobile.domain.data.model.*

// this way of definingsupports both platforms
// add your repositories here and then in your DI module call this classes for instanciation
open class GreetingRepository<T: Greeting>: SingleObjectRepository<T>()
open class BisqStatsRepository: SingleObjectRepository<BisqStats>()
open class BtcPriceRepository: SingleObjectRepository<BtcPrice>()
open class SettingsRepository: SingleObjectRepository<Settings>()

open class MyTradesRepository : SingleObjectRepository<MyTrades>() {
init {
runBlocking {
val myTrades = MyTrades(
trades = listOf(
BisqOffer(id = "offer1", isBuy = true, price = 95000, currency = "USD"),
BisqOffer(id = "offer2", isBuy = false, price = 96000, currency = "USD"),
BisqOffer(id = "offer3", isBuy = true, price = 97000, currency = "USD"),
BisqOffer(id = "offer4", isBuy = false, price = 98000, currency = "USD"),
BisqOffer(id = "offer5", isBuy = true, price = 99000, currency = "USD"),
BisqOffer(id = "offer1", isBuy = true, price = 95000, currency = "USD"),
BisqOffer(id = "offer2", isBuy = false, price = 96000, currency = "USD"),
BisqOffer(id = "offer3", isBuy = true, price = 97000, currency = "USD"),
BisqOffer(id = "offer4", isBuy = false, price = 98000, currency = "USD"),
BisqOffer(id = "offer5", isBuy = true, price = 99000, currency = "USD")
)
)

create(myTrades)

println("MyTradeRepo :: Created")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package network.bisq.mobile.domain.di

import network.bisq.mobile.domain.data.model.Greeting
import network.bisq.mobile.domain.data.repository.BisqStatsRepository
import network.bisq.mobile.domain.data.repository.BtcPriceRepository
import network.bisq.mobile.domain.data.repository.GreetingRepository
import network.bisq.mobile.domain.data.repository.SettingsRepository
import network.bisq.mobile.domain.data.repository.*
import org.koin.dsl.module

val domainModule = module {
single<GreetingRepository<Greeting>> { GreetingRepository() }
single<BisqStatsRepository> { BisqStatsRepository() }
single<BtcPriceRepository> { BtcPriceRepository() }
single<SettingsRepository> { SettingsRepository() }
single<MyTradesRepository> { MyTradesRepository() }
single<CurrenciesRepository> { CurrenciesRepository() }
}
2 changes: 2 additions & 0 deletions bisqapps/shared/presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ kotlin {
implementation(libs.navigation.compose)
implementation(libs.lyricist)

implementation(libs.coil.compose)

}
val commonTest by getting {
dependencies {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,10 @@ val EnStrings = Strings(
buttons_next = "Next",
buttons_submit = "Submit",
buttons_cancel = "Cancel",

common_offers = "Offers",
common_search = "Search",

offers_list_buy_from = "Buy from",
offers_list_sell_to = "Sell to",
)
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,10 @@ val FRStrings = Strings(
buttons_next = "[FR] Next",
buttons_submit = "[FR] Submit",
buttons_cancel = "[FR] Cancel",

common_offers = "[FR] offers",
common_search = "[FR] Search",

offers_list_buy_from = "[FR] Buy from",
offers_list_sell_to = "[FR] Sell to",
)
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,10 @@ data class Strings(
val buttons_next: String,
val buttons_submit: String,
val buttons_cancel: String,

val common_offers: String,
val common_search: String,

val offers_list_buy_from: String,
val offers_list_sell_to: String,
)
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package network.bisq.mobile.presentation.di

import androidx.navigation.NavController
import androidx.navigation.NavHostController
import network.bisq.mobile.presentation.MainPresenter
import network.bisq.mobile.presentation.ui.AppPresenter
import network.bisq.mobile.presentation.ui.uicases.GettingStartedPresenter
import network.bisq.mobile.presentation.ui.uicases.IGettingStarted
import network.bisq.mobile.presentation.ui.uicases.offers.CurrencyListPresenter
import network.bisq.mobile.presentation.ui.uicases.offers.ICurrencyList
import network.bisq.mobile.presentation.ui.uicases.offers.IOffersList
import network.bisq.mobile.presentation.ui.uicases.offers.OffersListPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.CreateProfilePresenter
import network.bisq.mobile.presentation.ui.uicases.startup.IOnboardingPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.ITrustedNodeSetupPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.OnBoardingPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.SplashPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.TrustedNodeSetupPresenter
import network.bisq.mobile.presentation.ui.uicases.trades.IMyTrades
import network.bisq.mobile.presentation.ui.uicases.trades.MyTradesPresenter
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.module
Expand Down Expand Up @@ -55,4 +62,16 @@ val presentationModule = module {
settingsRepository = get()
)
} bind ITrustedNodeSetupPresenter::class

single { CurrencyListPresenter(get(), get()) } bind ICurrencyList::class

single { OffersListPresenter(get()) } bind IOffersList::class

single {
(navController: NavController, tabController: NavController) -> MyTradesPresenter(
get(),
tabController = tabController,
myTradesRepository = get()
)
} bind IMyTrades::class
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,69 @@
package network.bisq.mobile.presentation.ui.components

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import bisqapps.shared.presentation.generated.resources.Res
import cafe.adriel.lyricist.LocalStrings
import coil3.compose.AsyncImage
import network.bisq.mobile.domain.data.model.FiatCurrency
import network.bisq.mobile.presentation.ui.components.atoms.BisqText
import network.bisq.mobile.presentation.ui.components.atoms.DynamicImage
import network.bisq.mobile.presentation.ui.theme.BisqTheme
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource


@OptIn(ExperimentalResourceApi::class)
@Composable
fun CurrencyProfileCard(currencyName: String, currencyShort: String, image: DrawableResource) {
fun CurrencyProfileCard(
currency: FiatCurrency,
onClick: (FiatCurrency) -> Unit) {

val strings = LocalStrings.current
val interactionSource = remember { MutableInteractionSource() }

Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 14.dp, vertical = 4.dp)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = {
onClick(currency)
}
)
,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Image(painterResource(image), null, modifier = Modifier.size(36.dp))
DynamicImage("drawable/${currency.flagImage}", contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Column {
BisqText.baseRegular(
text = currencyName,
text = currency.name,
color = BisqTheme.colors.light1,
)
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(0.dp))
BisqText.baseRegular(
text = currencyShort,
text = currency.code,
color = BisqTheme.colors.grey2,
)
}
}
BisqText.smallRegular(
text = "43 offers",
text = "${currency.offerCount.toString()} ${strings.common_offers}",
color = BisqTheme.colors.primary,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package network.bisq.mobile.presentation.ui.components.atoms

import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import bisqapps.shared.presentation.generated.resources.Res
import bisqapps.shared.presentation.generated.resources.bisq_logo
import bisqapps.shared.presentation.generated.resources.currency_aed
import coil3.compose.AsyncImage
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource


// The idea of this Composable is to load images at run time with a string path.
// TODO: In case the image doesn't exist, it should be handled gracefully
@OptIn(ExperimentalResourceApi::class)
@Composable
fun DynamicImage(path: String, contentDescription: String?, modifier: Modifier? = Modifier) {
AsyncImage(
model = Res.getUri(path),
//model = Res.getUri("drawable/currency_usd.png"),
//fallback = painterResource(Res.drawable.currency_aed),
contentDescription = null,
modifier = Modifier.size(36.dp),
onError = {
println("Error")
}
)
}
Loading

0 comments on commit 3c8ce93

Please sign in to comment.