Skip to content

Commit

Permalink
Check notification permission on new pair alert (#118)
Browse files Browse the repository at this point in the history
* Check notification permission on new pair alert

* fix frequent sorting

* Ask notification permission immediately if pair alerts already exist
  • Loading branch information
mdrlzy authored Oct 11, 2024
1 parent 3ee6134 commit 6400e4f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.arkbuilders.rate.data.permission

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class NotificationPermissionHelper @Inject constructor(
private val ctx: Context,
) {
fun isGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(ctx, Manifest.permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
} else {
true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CalcFrequentCurrUseCase @Inject constructor(
private fun mapToSortRating(list: List<CodeUseStat>): List<Pair<CurrencyCode, Double>> {
val now = OffsetDateTime.now()
return list.map { stat ->
val daysPassed = Duration.between(now, stat.lastUsedDate).toDays()
val daysPassed = Duration.between(stat.lastUsedDate, now).toDays()
val timeFactor = daysPassed * 0.5
stat.code to stat.count.toDouble() - timeFactor
}.toList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

package dev.arkbuilders.rate.presentation.pairalert

import android.Manifest
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -69,17 +73,41 @@ import timber.log.Timber
@Destination
@Composable
fun PairAlertConditionScreen(navigator: DestinationsNavigator) {
val ctx = LocalContext.current
val notificationPermissionLauncher =
rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted ->
if (isGranted.not()) {
Toast.makeText(
ctx,
ctx.getString(R.string.alert_post_notification_permission_explanation),
Toast.LENGTH_SHORT,
).show()
}
}

val viewModel: PairAlertViewModel =
viewModel(factory = DIManager.component.pairAlertVMFactory())

val state by viewModel.collectAsState()
val snackState = remember { SnackbarHostState() }
val ctx = LocalContext.current

val isEmpty = state.pages.isEmpty()

viewModel.collectSideEffect { effect ->
when (effect) {
is PairAlertEffect.NavigateToAdd ->
navigator.navigate(
AddPairAlertScreenDestination(
pairAlertId = effect.pairId,
),
)

PairAlertEffect.AskNotificationPermission -> {
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

is PairAlertEffect.ShowSnackbarAdded ->
snackState.showSnackbar(effect.visuals)

Expand Down Expand Up @@ -118,9 +146,7 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) {
contentColor = Color.White,
containerColor = ArkColor.Secondary,
shape = CircleShape,
onClick = {
navigator.navigate(AddPairAlertScreenDestination())
},
onClick = viewModel::onNewPair,
) {
Icon(Icons.Default.Add, contentDescription = "")
}
Expand All @@ -137,13 +163,13 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) {
when {
state.noInternet -> NoInternetScreen(viewModel::onRefreshClick)
state.initialized.not() -> LoadingScreen()
isEmpty -> Empty(navigator)
isEmpty -> Empty(navigator, onNewPair = viewModel::onNewPair)
else ->
Content(
state,
onDelete = viewModel::onDelete,
onClick = { pair ->
navigator.navigate(AddPairAlertScreenDestination(pair.id))
viewModel.onNewPair(pair.id)
},
onEnableToggle = viewModel::onEnableToggle,
)
Expand Down Expand Up @@ -337,7 +363,10 @@ private fun PairAlertItem(
}

@Composable
private fun Empty(navigator: DestinationsNavigator) {
private fun Empty(
navigator: DestinationsNavigator,
onNewPair: () -> Unit,
) {
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.align(Alignment.Center),
Expand Down Expand Up @@ -365,9 +394,7 @@ private fun Empty(navigator: DestinationsNavigator) {
)
AppButton(
modifier = Modifier.padding(top = 24.dp),
onClick = {
navigator.navigate(AddPairAlertScreenDestination())
},
onClick = { onNewPair() },
) {
Icon(
painter = painterResource(id = R.drawable.ic_add),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dev.arkbuilders.rate.presentation.pairalert
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import dev.arkbuilders.rate.data.permission.NotificationPermissionHelper
import dev.arkbuilders.rate.domain.model.PairAlert
import dev.arkbuilders.rate.domain.repo.AnalyticsManager
import dev.arkbuilders.rate.domain.repo.CurrencyRepo
Expand Down Expand Up @@ -33,6 +34,10 @@ data class PairAlertScreenState(
)

sealed class PairAlertEffect {
data class NavigateToAdd(val pairId: Long? = null) : PairAlertEffect()

data object AskNotificationPermission : PairAlertEffect()

data class ShowSnackbarAdded(
val visuals: NotifyAddedSnackbarVisuals,
) : PairAlertEffect()
Expand All @@ -44,6 +49,7 @@ class PairAlertViewModel(
private val pairAlertRepo: PairAlertRepo,
private val currencyRepo: CurrencyRepo,
private val analyticsManager: AnalyticsManager,
private val notificationPermissionHelper: NotificationPermissionHelper,
) : ViewModel(), ContainerHost<PairAlertScreenState, PairAlertEffect> {
override val container: Container<PairAlertScreenState, PairAlertEffect> =
container(
Expand All @@ -53,6 +59,14 @@ class PairAlertViewModel(
init {
analyticsManager.trackScreen("PairAlertScreen")

intent {
if (pairAlertRepo.getAll().isNotEmpty() &&
notificationPermissionHelper.isGranted().not()
) {
postSideEffect(PairAlertEffect.AskNotificationPermission)
}
}

intent {
if (currencyRepo.isRatesAvailable().not()) {
reduce {
Expand Down Expand Up @@ -91,6 +105,15 @@ class PairAlertViewModel(
}.launchIn(viewModelScope)
}

fun onNewPair(pairId: Long? = null) =
intent {
if (notificationPermissionHelper.isGranted()) {
postSideEffect(PairAlertEffect.NavigateToAdd(pairId))
} else {
postSideEffect(PairAlertEffect.AskNotificationPermission)
}
}

fun onRefreshClick() =
intent {
reduce { state.copy(noInternet = false) }
Expand Down Expand Up @@ -127,12 +150,14 @@ class PairAlertViewModelFactory @Inject constructor(
private val pairAlertRepo: PairAlertRepo,
private val currencyRepo: CurrencyRepo,
private val analyticsManager: AnalyticsManager,
private val notificationPermissionHelper: NotificationPermissionHelper,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PairAlertViewModel(
pairAlertRepo,
currencyRepo,
analyticsManager,
notificationPermissionHelper,
) as T
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<string name="alert_empty_title">No Alerts at the Moment</string>
<string name="alert_empty_desc">Stay updated! We\'ll post any important notifications or changes in exchange rates here.</string>
<string name="new_alert">New Alert</string>
<string name="alert_post_notification_permission_explanation">Application requires permission to post pair alert notifications.</string>
<string name="alert_snackbar_new_title">Alert for %1$s has been created</string>
<string name="alert_snackbar_new_desc">You’ll get notified when %1$s price is %2$s %3$s %4$s</string>
<string name="alert_snackbar_removed_title">Alert for %1$s has been deleted</string>
Expand Down

0 comments on commit 6400e4f

Please sign in to comment.