Skip to content

Commit

Permalink
Updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanarodr committed May 5, 2024
1 parent 94f401d commit ee0cf0f
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 41 deletions.
9 changes: 9 additions & 0 deletions design-system/src/main/res/drawable/ic_remove.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M240,520q-17,0 -28.5,-11.5T200,480q0,-17 11.5,-28.5T240,440h480q17,0 28.5,11.5T760,480q0,17 -11.5,28.5T720,520L240,520Z"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import br.com.stonks.feature.stocks.domain.mapper.StockAlertModelToUiMapper
import br.com.stonks.feature.stocks.domain.mapper.StockAlertResponseToEntityMapper
import br.com.stonks.feature.stocks.domain.mapper.StockAlertResponseToModelMapper
import br.com.stonks.feature.stocks.domain.usecase.StockAlertUseCase
import br.com.stonks.feature.stocks.domain.usecase.StockPriceComparatorUseCase
import br.com.stonks.feature.stocks.repository.StockRepository
import br.com.stonks.feature.stocks.repository.StockRepositoryImpl
import br.com.stonks.feature.stocks.repository.local.StockLocalDataSource
Expand Down Expand Up @@ -84,9 +85,16 @@ val stockModule = module {
)
}

factory {
StockPriceComparatorUseCase(
stockRepository = get(),
)
}

factory {
StockAlertUseCase(
stockAlertRepository = get(),
stockRepository = get(),
stockPriceComparatorUseCase = get(),
stockAlertModelMapper = get(),
stockAlertResponseMapper = get(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import br.com.stonks.feature.stocks.domain.model.StockAlertModel
import br.com.stonks.feature.stocks.ui.model.AlertUiModel
import br.com.stonks.feature.stocks.ui.model.StockAlertUiModel
import br.com.stonks.feature.stocks.utils.getColor
import br.com.stonks.feature.stocks.utils.getIcon

internal class StockAlertModelToUiMapper : Mapper<List<StockAlertModel>, StockAlertUiModel> {

Expand All @@ -19,6 +20,8 @@ internal class StockAlertModelToUiMapper : Mapper<List<StockAlertModel>, StockAl
alertValue = input.alertValue,
status = input.status,
alert = input.notificationTrigger,
alertColor = input.notificationTrigger.getColor(),
alertIcon = input.notificationTrigger.getIcon(),
tagColor = input.status.getColor(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import br.com.stonks.feature.stocks.domain.types.StockAlertType
import br.com.stonks.feature.stocks.domain.types.StockStatusType
import br.com.stonks.feature.stocks.repository.remote.response.StockAlertResponse

internal class StockAlertResponseToModelMapper : Mapper<List<StockAlertResponse>, List<StockAlertModel>> {
internal class StockAlertResponseToModelMapper : Mapper<StockAlertResponse, StockAlertModel> {

override fun mapper(input: List<StockAlertResponse>): List<StockAlertModel> {
return input.map(::mapperStockAlert)
override fun mapper(input: StockAlertResponse): StockAlertModel {
return mapperStockAlert(input)
}

private fun mapperStockAlert(input: StockAlertResponse) = StockAlertModel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,32 @@ import br.com.stonks.feature.stocks.repository.StockRepository
import kotlinx.coroutines.flow.Flow

internal class StockAlertUseCase(
private val stockAlertRepository: StockRepository,
private val stockRepository: StockRepository,
private val stockPriceComparatorUseCase: StockPriceComparatorUseCase,
private val stockAlertModelMapper: StockAlertResponseToModelMapper,
private val stockAlertResponseMapper: StockAlertModelToResponseMapper,
) {

suspend fun getRemoteStockAlerts(): Flow<List<StockAlertModel>> {
return stockAlertRepository.getRemoteStockAlerts().mapCatching {
stockAlertModelMapper.mapper(it)
}.asFlow()
}

suspend fun listStockAlerts(): Flow<List<StockAlertModel>> {
return stockAlertRepository.listStockAlerts().mapCatching {
stockAlertModelMapper.mapper(it)
return stockRepository.listStockAlerts().mapCatching { results ->
results.map {
val alertType = stockPriceComparatorUseCase.checkStockPrice(it)
stockAlertModelMapper.mapper(it.copy(notificationTrigger = alertType.status))
}
}.asFlow()
}

suspend fun saveStockAlert(alert: StockAlertModel): Flow<Unit> {
val alertResponse = stockAlertResponseMapper.mapper(alert)

return if (alert.id == DefaultPrimaryKey) {
stockAlertRepository.insertStockAlert(alertResponse)
stockRepository.insertStockAlert(alertResponse)
} else {
stockAlertRepository.updateStockAlert(alertResponse)
stockRepository.updateStockAlert(alertResponse)
}.asFlow()
}

suspend fun deleteStockAlert(alertId: Long): Flow<Unit> {
return stockAlertRepository.deleteStockAlert(alertId).asFlow()
return stockRepository.deleteStockAlert(alertId).asFlow()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package br.com.stonks.feature.stocks.domain.usecase

import br.com.stonks.feature.stocks.domain.types.StockAlertType
import br.com.stonks.feature.stocks.repository.StockRepository
import br.com.stonks.feature.stocks.repository.remote.response.StockAlertResponse
import timber.log.Timber

internal class StockPriceComparatorUseCase(
private val stockRepository: StockRepository,
) {

private val stocks: MutableList<StockAlertResponse> = mutableListOf()

private suspend fun fetchRemoteStock(): MutableList<StockAlertResponse> {
if (stocks.isEmpty()) {
stocks.addAll(
stockRepository.getRemoteStockAlerts().getOrDefault(emptyList())
)
}

return stocks
}

private fun searchStockAlert(ticket: String): StockAlertResponse? {
return stocks.find { it.stockTicket == ticket }?.also {
Timber.d("Alert found for the stock ${it.stockTicket}")
}
}

private fun calculatePerformance(alertPrice: Double, currentPrice: Double): StockAlertType {
return if (alertPrice == currentPrice) {
StockAlertType.UNKNOWN
} else if (alertPrice < currentPrice) {
StockAlertType.HIGH_PRICE
} else {
StockAlertType.LOW_PRICE
}
}

suspend fun checkStockPrice(alert: StockAlertResponse): StockAlertType {
fetchRemoteStock()

return searchStockAlert(alert.stockTicket)?.run {
calculatePerformance(
alertPrice = alert.alertValue,
currentPrice = this.alertValue,
)
} ?: StockAlertType.UNKNOWN
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ internal data class AlertUiModel(
val alertValue: Double,
val status: StockStatusType,
val alert: StockAlertType = StockAlertType.UNKNOWN,
val alertColor: Color = ColorToken.Grayscale200,
val alertIcon: Int = br.com.stonks.designsystem.R.drawable.ic_remove,
val tagColor: Color = ColorToken.Grayscale200,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ 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.foundation.layout.wrapContentHeight
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
Expand All @@ -19,6 +20,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand All @@ -34,7 +36,9 @@ import br.com.stonks.feature.stocks.R
import br.com.stonks.feature.stocks.domain.types.StockAlertType
import br.com.stonks.feature.stocks.domain.types.StockStatusType
import br.com.stonks.feature.stocks.ui.model.AlertUiModel
import br.com.stonks.feature.stocks.utils.getColor
import br.com.stonks.feature.stocks.utils.getDescription
import br.com.stonks.feature.stocks.utils.getIcon

@Composable
@Suppress("MultipleEmitters")
Expand Down Expand Up @@ -106,7 +110,7 @@ private fun AlertLayoutTitle(

@Composable
private fun AlertLayoutContent(
alertValue: Double,
uiModel: AlertUiModel,
modifier: Modifier = Modifier,
) {
Row(
Expand All @@ -121,20 +125,33 @@ private fun AlertLayoutContent(
contentScale = ContentScale.FillWidth
)
Column(
modifier = Modifier.padding(start = SpacingToken.lg),
modifier = Modifier
.weight(1f)
.padding(start = SpacingToken.lg),
) {
Text(
text = stringResource(id = R.string.alert_price),
style = MaterialTheme.typography.titleSmall,
color = ColorToken.Grayscale300,
)
Spacer(modifier = Modifier.height(SpacingToken.xs))
Text(
text = alertValue.formatCurrency(),
style = MaterialTheme.typography.titleSmall.copy(
fontWeight = FontWeight.ExtraBold,
),
)
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = uiModel.alertValue.formatCurrency(),
style = MaterialTheme.typography.titleSmall.copy(
fontWeight = FontWeight.ExtraBold,
),
)
Spacer(modifier = Modifier.width(SpacingToken.xs))
Image(
modifier = Modifier.size(18.dp),
painter = painterResource(id = uiModel.alert.getIcon()),
colorFilter = ColorFilter.tint(uiModel.alert.getColor()),
contentDescription = null,
)
}
}
}
}
Expand Down Expand Up @@ -166,7 +183,7 @@ internal fun AlertCard(
color = ColorToken.Grayscale100,
)
AlertLayoutContent(
alertValue = uiModel.alertValue,
uiModel = uiModel,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package br.com.stonks.feature.stocks.utils

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.ui.graphics.Color
import br.com.stonks.designsystem.tokens.ColorToken
import br.com.stonks.feature.stocks.R
import br.com.stonks.feature.stocks.domain.types.StockAlertType
import br.com.stonks.feature.stocks.domain.types.StockStatusType

internal fun StockStatusType.getColor(): Color {
Expand All @@ -22,3 +24,20 @@ internal fun StockStatusType.getDescription(): Int {
StockStatusType.UNKNOWN -> R.string.status_unknown
}
}

internal fun StockAlertType.getColor(): Color {
return when (this) {
StockAlertType.HIGH_PRICE -> ColorToken.HighlightGreen
StockAlertType.LOW_PRICE -> ColorToken.HighlightRed
StockAlertType.UNKNOWN -> ColorToken.Grayscale200
}
}

@DrawableRes
internal fun StockAlertType.getIcon(): Int {
return when (this) {
StockAlertType.HIGH_PRICE -> br.com.stonks.designsystem.R.drawable.ic_arrow_up
StockAlertType.LOW_PRICE -> br.com.stonks.designsystem.R.drawable.ic_arrow_down
StockAlertType.UNKNOWN -> br.com.stonks.designsystem.R.drawable.ic_remove
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class StockAlertModelToUiMapperTest {
id = 1L,
ticket = "lorem",
alertValue = 1.5,
alertColor = ColorToken.HighlightGreen,
alertIcon = br.com.stonks.designsystem.R.drawable.ic_arrow_up,
status = StockStatusType.AVAILABLE,
alert = StockAlertType.HIGH_PRICE,
tagColor = ColorToken.HighlightGreen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,19 @@ class StockAlertResponseToModelMapperTest {
assertEquals(expected, result)
}

private fun createInputData() = listOf(
StockAlertResponse(
id = 1L,
stockTicket = "lorem",
alertValue = 5.0,
status = "available",
notificationTrigger = "low",
)
private fun createInputData() = StockAlertResponse(
id = 1L,
stockTicket = "lorem",
alertValue = 5.0,
status = "available",
notificationTrigger = "low",
)

private fun createExpectedData() = listOf(
StockAlertModel(
id = 1L,
ticket = "lorem",
alertValue = 5.0,
status = StockStatusType.AVAILABLE,
notificationTrigger = StockAlertType.LOW_PRICE,
)
private fun createExpectedData() = StockAlertModel(
id = 1L,
ticket = "lorem",
alertValue = 5.0,
status = StockStatusType.AVAILABLE,
notificationTrigger = StockAlertType.LOW_PRICE,
)
}
Loading

0 comments on commit ee0cf0f

Please sign in to comment.