NTP-8307 | Kamlesh | added notifyWidget and Bottomsheets (#13877)

This commit is contained in:
Kamalesh Garnayak
2024-12-18 02:16:56 +05:30
committed by GitHub
parent 32626d1b4a
commit d2bdcf9a8f
28 changed files with 900 additions and 10 deletions

View File

@@ -94,7 +94,7 @@ mockk = "1.13.10"
mvel2 = "2.4.15.Final"
navi-adverse = "1.11.0"
navi-alfred = "2.1.0"
navi-elex = "1.9.0"
navi-elex = "1.9.1"
navi-guarddog = "3.12.0"
navi-pulse = "1.14.0"
navi-uitron = "2.5.0"

View File

@@ -22,8 +22,11 @@ import com.navi.common.constants.DBCacheConstants
import com.navi.common.forge.model.ScreenDefinition
import com.navi.common.model.ModuleName
import com.navi.common.model.ModuleNameV2
import com.navi.common.model.NotificationSettings
import com.navi.common.model.NotificationSettingsRequest
import com.navi.common.model.UploadDataAsyncResponse
import com.navi.common.network.models.RepoResult
import com.navi.common.network.retrofit.RetrofitService as CommonRetrofitService
import com.navi.common.utils.Constants.GZIP
import com.navi.rr.common.network.retrofit.ResponseHandler
import com.navi.rr.utils.cachemanager.CacheHandlerProxy
@@ -36,6 +39,7 @@ class CoinHomeScreenRepo
constructor(
private val responseHandler: ResponseHandler,
private val retrofitService: RetrofitService,
private val commonRetrofitService: CommonRetrofitService,
private val cacheHandlerProxy: CacheHandlerProxy,
) {
suspend fun fetchCoinHomeScreenUiTronConfigs(
@@ -147,6 +151,15 @@ constructor(
retrofitService.getRedemptionStatus(target = ModuleNameV2.COIN.name, txnId = txnId)
)
suspend fun updateNotificationsPermission(notificationSettings: List<NotificationSettings>) =
responseHandler.handleResponse(
response =
commonRetrofitService.updateNotificationsPermission(
notificationSettings =
NotificationSettingsRequest(global = notificationSettings)
)
)
suspend fun getCoinHomeScreenVariant(expName: String) =
cacheHandlerProxy.cacheManager.fetchAndCacheData(
key = DBCacheConstants.COIN_HOME_SCREEN_VARIANT_CACHE_KEY,

View File

@@ -15,7 +15,10 @@ import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.SCRATCH_CARD_HISTORY_HEADER
import com.navi.common.forge.model.ScreenDefinition
import com.navi.common.model.ModuleNameV2
import com.navi.common.model.NotificationSettings
import com.navi.common.model.NotificationSettingsRequest
import com.navi.common.network.models.RepoResult
import com.navi.common.network.retrofit.RetrofitService as CommonRetrofitService
import com.navi.common.utils.Constants.GZIP
import com.navi.rr.common.models.XTarget
import com.navi.rr.common.network.retrofit.ResponseHandler
@@ -28,6 +31,7 @@ class ScratchCardHistoryScreenRepo
constructor(
private val retrofitService: RetrofitService,
private val responseHandler: ResponseHandler,
private val commonRetrofitService: CommonRetrofitService,
private val cacheHandlerProxy: CacheHandlerProxy,
) {
suspend fun fetchScratchCardUiTronConfigs(
@@ -84,4 +88,13 @@ constructor(
)
)
}
suspend fun updateNotificationsPermission(notificationSettings: List<NotificationSettings>) =
responseHandler.handleResponse(
response =
commonRetrofitService.updateNotificationsPermission(
notificationSettings =
NotificationSettingsRequest(global = notificationSettings)
)
)
}

View File

@@ -29,6 +29,7 @@ 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.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridScope
@@ -95,13 +96,17 @@ import com.navi.coin.utils.ext.scaledSp
import com.navi.coin.utils.formatDuration
import com.navi.coin.vm.ScratchCardScreenVM
import com.navi.common.forge.model.WidgetModelDefinition
import com.navi.common.utils.toJsonObject
import com.navi.design.font.FontWeightEnum
import com.navi.design.font.getFontWeight
import com.navi.design.font.naviFontFamily
import com.navi.design.utils.NoRippleIndicationSource
import com.navi.naviwidgets.R as WidgetsR
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.rr.common.widgetFactory.NotifyWidgetRenderer
import com.navi.rr.common.widgetFactory.WidgetRenderer
import com.navi.rr.utils.NaviRRAnalytics
import com.navi.rr.utils.constants.Constants.SHOULD_NOTIFY_WIDGET
import com.navi.rr.utils.constants.EventConstants.BOTTOM_SHEET_TYPE
import com.navi.rr.utils.constants.EventConstants.SCRATCH_CARD_HISTORY_BOTTOM_SHEET_VISIBLE_EVENT
import com.navi.rr.utils.custompager.PagerItemState
@@ -126,6 +131,7 @@ fun ScratchCardListRenderer(
isScratchCardDisplayed: Boolean,
setScratchCardDisplayed: (Boolean) -> Unit,
pagerStates: PagerStateHolder<ScratchCard>,
jsonMetaData: Map<String, Any?>?,
) {
val analyticsHandler by remember { mutableStateOf(viewModel.analyticsHandler.Rewards()) }
val paginatedHistoryScreenState =
@@ -212,6 +218,38 @@ fun ScratchCardListRenderer(
)
)
}
val notifyWidgetDataMap =
jsonMetaData?.get("notifyWidgetData")?.toJsonObject()
val notifyWidgetTextData =
NotifyWidgetTextData(
successMainText =
notifyWidgetDataMap?.optString("successMainText"),
subText = notifyWidgetDataMap?.optString("subText"),
successSubText =
notifyWidgetDataMap?.optString("successSubText"),
mainText = notifyWidgetDataMap?.optString("mainText"),
ctaButtonText =
notifyWidgetDataMap?.optString("ctaButtonText"),
)
if (metadata?.get(SHOULD_NOTIFY_WIDGET)?.toBoolean() == true) {
Row(
modifier =
Modifier.background(Color_F9F9F9)
.fillMaxWidth()
.wrapContentHeight()
.padding(
bottom = 8.dp,
top = 8.dp,
)
) {
NotifyWidgetRenderer(
viewModel,
notifyWidgetTextData,
Color_F9F9F9
)
}
}
}
}
scratchCardList(

View File

@@ -7,7 +7,9 @@
package com.navi.coin.ui.compose.screen
import android.Manifest
import android.app.Activity
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.Spring
@@ -67,6 +69,7 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.navi.base.deeplink.DeepLinkManager
import com.navi.base.model.CtaData
@@ -89,7 +92,6 @@ import com.navi.coin.utils.constant.Constants
import com.navi.coin.utils.constant.Constants.BACKGROUND_IMAGE_HEIGHT_KEY
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.BACK_FROM
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.ONE_PROFILE_SCREEN
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.OPEN_APP_SETTINGS
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.TRIGGER_REDEMPTION_ACTION
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.TRIGGER_REDEMPTION_EVENT
import com.navi.coin.utils.constant.Constants.HERO_PLACEHOLDER_PROPERTY_KEY
@@ -103,8 +105,11 @@ import com.navi.common.managers.NaviLocationManager
import com.navi.common.navigation.NavArgs
import com.navi.common.navigation.clearResult
import com.navi.common.navigation.rememberNavigatorWithResultAndParams
import com.navi.common.permission.PermissionResult
import com.navi.common.permission.rememberMultiplePermissions
import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.uitron.model.action.ExecuteActionsCorrespondingToKey
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.design.utils.NoRippleIndicationSource
import com.navi.naviwidgets.R as WidgetsR
import com.navi.rr.R as rrR
@@ -116,7 +121,11 @@ import com.navi.rr.uitron.model.action.ScrollToAction
import com.navi.rr.utils.composeutils.InitWidgetActions
import com.navi.rr.utils.composeutils.brushType
import com.navi.rr.utils.constants.Constants.AUTO_REDEEM_KEY
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET_CLICKED
import com.navi.rr.utils.constants.Constants.NULL_STRING
import com.navi.rr.utils.constants.Constants.OPEN_APP_SETTINGS
import com.navi.rr.utils.constants.Constants.PERMISSIONS_GIVEN
import com.navi.rr.utils.constants.Constants.PERMISSION_BOTTOMSHEET
import com.navi.rr.utils.constants.Constants.TRUE
import com.navi.rr.utils.dpToPx
import com.navi.rr.utils.ext.toJson
@@ -133,7 +142,7 @@ import java.util.UUID
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@OptIn(ExperimentalFoundationApi::class, ExperimentalPermissionsApi::class)
@Composable
fun CoinHomeScreenV1(
bundle: Bundle? = null,
@@ -141,6 +150,8 @@ fun CoinHomeScreenV1(
coinHomeViewModelV1: CoinHomeViewModelV1 = hiltViewModel(),
naviCoinsAnalytics: NaviCoinsAnalytics.BasicEvent =
NaviCoinsAnalytics.naviCoinsAnalytics.BasicEvent(),
notifyMeAnalytics: CommonNaviAnalytics.NotifyMe =
CommonNaviAnalytics.naviAnalytics.NotifyMe(COIN_HOME_SCREEN)
) {
val scope = rememberCoroutineScope()
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -184,6 +195,7 @@ fun CoinHomeScreenV1(
navigateBackResult = result
context.navController.clearResult()
}
var shouldCallPermission by remember { mutableStateOf(false) }
fun shouldRefreshScreen() =
(coinHomeViewModelV1.handle.get<String>(BACK_FROM) == ONE_PROFILE_SCREEN ||
@@ -220,6 +232,23 @@ fun CoinHomeScreenV1(
}
}
val pushNotificationPermission =
rememberMultiplePermissions(permissions = listOf(Manifest.permission.POST_NOTIFICATIONS)) {
when (it) {
PermissionResult.AllGranted -> {
notifyMeAnalytics.notifyMeNudgePermissionGrantedEvent()
}
PermissionResult.HardDenied -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = false)
renderBottomSheet(PERMISSION_BOTTOMSHEET)
}
PermissionResult.ShowRationale -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = true)
}
PermissionResult.None -> {}
}
}
fun handleCtaAction(action: CtaAction) {
val screenUrl = action.ctaData?.url
val parameters = action.ctaData?.parameters
@@ -241,6 +270,17 @@ fun CoinHomeScreenV1(
OPEN_APP_SETTINGS -> {
openSettings(context)
}
NOTIFY_WIDGET_CLICKED -> {
notifyMeAnalytics.notifyMeNudgeClickEvent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pushNotificationPermission.launchMultiplePermissionRequest()
} else {
renderBottomSheet(PERMISSION_BOTTOMSHEET)
}
}
PERMISSIONS_GIVEN -> {
shouldCallPermission = true
}
else -> {
val id = UUID.randomUUID().toString()
val mutableListParams = parameters?.toMutableList()
@@ -395,6 +435,10 @@ fun CoinHomeScreenV1(
}
}
LaunchedEffect(shouldCallPermission) {
if (shouldCallPermission) coinHomeViewModelV1.updateNotificationsPermission()
}
InitActionHandler(activity = context, viewModel = coinHomeViewModelV1)
BackHandler {

View File

@@ -7,7 +7,9 @@
package com.navi.coin.ui.compose.screen
import android.Manifest
import android.app.Activity
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.FastOutSlowInEasing
@@ -85,9 +87,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.navi.base.model.CtaData
import com.navi.base.model.CtaType
import com.navi.base.sharedpref.PreferenceManager
import com.navi.base.utils.isNotNull
import com.navi.base.utils.orElse
import com.navi.base.utils.orFalse
@@ -108,7 +113,6 @@ import com.navi.coin.utils.constant.Constants
import com.navi.coin.utils.constant.Constants.BACK
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.BACK_FROM
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.ONE_PROFILE_SCREEN
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.OPEN_APP_SETTINGS
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.SHOW_BACK_LAYER
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.TRIGGER_REDEMPTION_ACTION
import com.navi.coin.utils.constant.Constants.CoinHomeScreen.TRIGGER_REDEMPTION_EVENT
@@ -129,10 +133,14 @@ import com.navi.common.navigation.NavArgs
import com.navi.common.navigation.NavigationAction
import com.navi.common.navigation.clearResult
import com.navi.common.navigation.rememberNavigatorWithResultAndParams
import com.navi.common.permission.PermissionResult
import com.navi.common.permission.rememberMultiplePermissions
import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.uitron.model.action.ExecuteActionsCorrespondingToKey
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.common.utils.getStatusBarHeight
import com.navi.design.utils.NoRippleIndicationSource
import com.navi.naviwidgets.utils.NOTIFY_WIDGET_DISMISSED_TIMESTAMP
import com.navi.rr.common.actions.InitActionHandler
import com.navi.rr.common.constants.COIN_HOME_SCREEN_V2
import com.navi.rr.common.widgetFactory.WidgetRenderer
@@ -141,6 +149,11 @@ import com.navi.rr.uitron.model.action.ScrollToAction
import com.navi.rr.uitron.render.RRCustomUiTronRenderer
import com.navi.rr.utils.composeutils.InitWidgetActions
import com.navi.rr.utils.constants.Constants.AUTO_REDEEM_KEY
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET_CLICKED
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET_DISMISSED
import com.navi.rr.utils.constants.Constants.OPEN_APP_SETTINGS
import com.navi.rr.utils.constants.Constants.PERMISSIONS_GIVEN
import com.navi.rr.utils.constants.Constants.PERMISSION_BOTTOMSHEET
import com.navi.rr.utils.constants.Constants.TRUE
import com.navi.rr.utils.dpToPx
import com.navi.rr.utils.ext.toJson
@@ -152,6 +165,7 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CoinHomeScreenV2(
bundle: Bundle? = null,
@@ -159,6 +173,8 @@ fun CoinHomeScreenV2(
viewModel: CoinHomeViewModelV2 = hiltViewModel(),
eventHandler: NaviCoinsAnalytics.BasicEvent =
NaviCoinsAnalytics.naviCoinsAnalytics.BasicEvent(),
notifyMeAnalytics: CommonNaviAnalytics.NotifyMe =
CommonNaviAnalytics.naviAnalytics.NotifyMe(COIN_HOME_SCREEN_V2)
) {
val scope = rememberCoroutineScope()
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -199,6 +215,13 @@ fun CoinHomeScreenV2(
context.navController.clearResult()
}
var permissionRequested by remember { mutableStateOf(false) }
val notificationPermissionState =
rememberPermissionState(permission = android.Manifest.permission.POST_NOTIFICATIONS) {
permissionRequested = true
}
var shouldCallPermission by remember { mutableStateOf(false) }
fun shouldRefreshScreen(): Boolean {
val backFrom = viewModel.handle.get<String>(BACK_FROM) == ONE_PROFILE_SCREEN
val autoRedemptionStarted = viewModel.handle.get<String>(AUTO_REDEMPTION_STARTED) == TRUE
@@ -216,6 +239,22 @@ fun CoinHomeScreenV2(
}
}
}
val pushNotificationPermission =
rememberMultiplePermissions(permissions = listOf(Manifest.permission.POST_NOTIFICATIONS)) {
when (it) {
PermissionResult.AllGranted -> {
notifyMeAnalytics.notifyMeNudgePermissionGrantedEvent()
}
PermissionResult.HardDenied -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = false)
renderBottomSheet(PERMISSION_BOTTOMSHEET)
}
PermissionResult.ShowRationale -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = true)
}
PermissionResult.None -> {}
}
}
fun sendLocationUpdates() {
if (viewModel.locationManager.isLocationOn(context)) {
@@ -256,6 +295,24 @@ fun CoinHomeScreenV2(
OPEN_APP_SETTINGS -> {
openSettings(context)
}
NOTIFY_WIDGET_CLICKED -> {
notifyMeAnalytics.notifyMeNudgeClickEvent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pushNotificationPermission.launchMultiplePermissionRequest()
} else {
renderBottomSheet(PERMISSION_BOTTOMSHEET)
}
}
PERMISSIONS_GIVEN -> {
shouldCallPermission = true
}
NOTIFY_WIDGET_DISMISSED -> {
notifyMeAnalytics.notifyMeNudgeDismissEvent()
PreferenceManager.setStringPreferenceApp(
NOTIFY_WIDGET_DISMISSED_TIMESTAMP,
System.currentTimeMillis().toString()
)
}
SHOW_BACK_LAYER -> {
viewModel.setShowBackLayer(true)
}

View File

@@ -7,7 +7,9 @@
package com.navi.coin.ui.compose.screen
import android.Manifest
import android.app.Activity
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.AnimationSpec
@@ -58,6 +60,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.navi.base.deeplink.DeepLinkManager
import com.navi.base.deeplink.util.DeeplinkConstants.PRODUCT_HELP_PAGE
@@ -87,7 +90,10 @@ import com.navi.coin.utils.constant.ScratchCardAnimationConstants.SCRATCH_CARD_I
import com.navi.coin.utils.shape.SemiCircleShape
import com.navi.coin.vm.ScratchCardScreenVM
import com.navi.common.R as CommonR
import com.navi.common.permission.PermissionResult
import com.navi.common.permission.rememberMultiplePermissions
import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.common.utils.EMPTY
import com.navi.common.utils.getScreenHeight
import com.navi.design.font.FontWeightEnum
@@ -104,10 +110,15 @@ import com.navi.rr.scratchcard.model.ScratchCardBackResponse
import com.navi.rr.scratchcard.model.ScratchCardResponse
import com.navi.rr.scratchcard.ui.compose.ScratchCardRenderer
import com.navi.rr.utils.NaviRRAnalytics
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET_CLICKED
import com.navi.rr.utils.constants.Constants.OPEN_APP_SETTINGS
import com.navi.rr.utils.constants.Constants.PERMISSIONS_GIVEN
import com.navi.rr.utils.constants.Constants.PERMISSION_BOTTOMSHEET
import com.navi.rr.utils.constants.EventConstants
import com.navi.rr.utils.custompager.PagerStateHolder
import com.navi.rr.utils.ext.clickable
import com.navi.rr.utils.filterAndPrioritize
import com.navi.rr.utils.openSettings
import com.navi.uitron.utils.setShimmerEffect
import com.navi.uitron.utils.toPx
import com.ramcosta.composedestinations.annotation.Destination
@@ -115,13 +126,15 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
@Destination
@Composable
fun ScratchCardHistoryScreen(
bundle: Bundle? = null,
navigator: DestinationsNavigator,
viewModel: ScratchCardScreenVM = hiltViewModel(),
notifyMeAnalytics: CommonNaviAnalytics.NotifyMe =
CommonNaviAnalytics.naviAnalytics.NotifyMe(SCRATCH_CARD_HISTORY_SCREEN)
) {
val context = LocalContext.current as CoinBaseActivity
@@ -136,6 +149,8 @@ fun ScratchCardHistoryScreen(
val nextScratchCard by
viewModel.scratchCardUseCases.nextScratchCard.collectAsStateWithLifecycle()
var shouldCallPermission by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.navigateToNextScreen.collect { navigator.navigate(direction = it) }
}
@@ -143,6 +158,35 @@ fun ScratchCardHistoryScreen(
LaunchedEffect(Unit) {
viewModel.navigateToPreviousScreen.collect { if (it) navigator.navigateUp() }
}
fun renderNewBottomSheet(bottomSheetId: String?) {
coroutineScope.launch(Dispatchers.IO) {
bottomSheetId?.let {
viewModel.getBottomSheetData(bottomSheetId)?.apply {
viewModel.updateBottomSheetUIState(
bottomSheetState = RRBottomSheetStateHolder.RRBottomSheetState.Visible,
bottomSheetUIContent = this
)
}
}
}
}
val pushNotificationPermission =
rememberMultiplePermissions(permissions = listOf(Manifest.permission.POST_NOTIFICATIONS)) {
when (it) {
PermissionResult.AllGranted -> {
notifyMeAnalytics.notifyMeNudgePermissionGrantedEvent()
}
PermissionResult.HardDenied -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = false)
renderNewBottomSheet(PERMISSION_BOTTOMSHEET)
}
PermissionResult.ShowRationale -> {
notifyMeAnalytics.notifyMeNudgePermissionDeniedEvent(inAppAllowable = true)
}
PermissionResult.None -> {}
}
}
LaunchedEffect(Unit) {
viewModel.ctaNavigation.collect { action ->
@@ -172,6 +216,20 @@ fun ScratchCardHistoryScreen(
}
}
}
OPEN_APP_SETTINGS -> {
openSettings(context)
}
NOTIFY_WIDGET_CLICKED -> {
notifyMeAnalytics.notifyMeNudgeClickEvent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pushNotificationPermission.launchMultiplePermissionRequest()
} else {
renderNewBottomSheet(PERMISSION_BOTTOMSHEET)
}
}
PERMISSIONS_GIVEN -> {
shouldCallPermission = true
}
else ->
DeepLinkManager.getDeepLinkListener()
?.navigateTo(
@@ -201,6 +259,10 @@ fun ScratchCardHistoryScreen(
}
}
LaunchedEffect(shouldCallPermission) {
if (shouldCallPermission) viewModel.updateNotificationsPermission()
}
BackHandler {
viewModel.countDownHelper.cancelAllTimers()
if (!navigator.navigateUp()) {
@@ -509,7 +571,8 @@ fun ScratchCardHistorySuccessScreen(
metadata = state.data.metaData,
isScratchCardDisplayed = isScratchCardDisplayed,
setScratchCardDisplayed = setScratchCardDisplayed,
pagerStates = pagerStates
pagerStates = pagerStates,
jsonMetaData = state.data.jsonMetaData
)
},
backgroundColor = Color.Transparent

View File

@@ -83,7 +83,6 @@ object Constants {
}
object CoinHomeScreen {
const val OPEN_APP_SETTINGS = "OPEN_APP_SETTINGS"
const val TRIGGER_REDEMPTION_EVENT = "trigger_redemption"
const val TRIGGER_REDEMPTION_ACTION = "trigger_redemption_action"
const val ONE_PROFILE_VERIFICATION_REQUEST_CODE = 1001

View File

@@ -43,6 +43,8 @@ import com.navi.common.constants.FAILED
import com.navi.common.forge.model.ScreenDefinition
import com.navi.common.forge.model.ScreenStructure
import com.navi.common.forge.model.WidgetModelDefinition
import com.navi.common.model.NotificationSettings
import com.navi.common.model.SettingsMedium
import com.navi.common.network.ApiConstants
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.uitron.model.action.CtaAction
@@ -547,6 +549,12 @@ constructor(
)
}
suspend fun updateNotificationsPermission() {
val notificationSettings =
listOf(NotificationSettings(medium = SettingsMedium.PUSH_NOTIFICATION, enabled = true))
coinHomeScreenRepo.updateNotificationsPermission(notificationSettings)
}
private companion object {
val SHAREABILITY_URLS =
listOf(

View File

@@ -20,6 +20,8 @@ import com.navi.coin.utils.constant.Constants
import com.navi.common.checkmate.model.MetricInfo
import com.navi.common.forge.model.ScreenDefinition
import com.navi.common.forge.model.WidgetModelDefinition
import com.navi.common.model.NotificationSettings
import com.navi.common.model.SettingsMedium
import com.navi.common.network.ApiConstants
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.uitron.model.action.CtaAction
@@ -359,6 +361,12 @@ constructor(
}
}
suspend fun updateNotificationsPermission() {
val notificationSettings =
listOf(NotificationSettings(medium = SettingsMedium.PUSH_NOTIFICATION, enabled = true))
scratchCardHistoryScreenRepo.updateNotificationsPermission(notificationSettings)
}
private companion object {
const val ONE_DAY_IN_MILLI_SECONDS = 24 * 60 * 60 * 1000
const val COIN_TITLE = "coinTitle"

View File

@@ -0,0 +1,25 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.common.model
import com.google.gson.annotations.SerializedName
data class NotificationSettingsRequest(
@SerializedName("global") var global: List<NotificationSettings>? = null
)
data class NotificationSettings(
@SerializedName("medium") val medium: SettingsMedium? = null,
@SerializedName("enabled") var enabled: Boolean? = null,
)
enum class SettingsMedium(val title: String) {
WHATSAPP("WhatsApp"),
SMS("SMS"),
PUSH_NOTIFICATION("Push notifications")
}

View File

@@ -28,6 +28,7 @@ import com.navi.common.model.CommunicationAppLaunchData
import com.navi.common.model.DeviceDetail
import com.navi.common.model.FeedbackResponse
import com.navi.common.model.FeedbackSubmitData
import com.navi.common.model.NotificationSettingsRequest
import com.navi.common.model.SubmitPermissionRequestData
import com.navi.common.model.SubmitPermissionResponse
import com.navi.common.model.UploadDataAsyncResponse
@@ -229,4 +230,9 @@ interface RetrofitService {
suspend fun fetchTemporarySessionToken(
@Body request: RedirectionAuthTokenRequest
): Response<GenericResponse<RedirectionAuthTokenResponse>>
@PATCH("/v1/communication-profile")
suspend fun updateNotificationsPermission(
@Body notificationSettings: NotificationSettingsRequest,
): Response<GenericResponse<Unit>>
}

View File

@@ -10,6 +10,7 @@ package com.navi.rr.common.deserializer
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonElement
import com.navi.common.uitron.deserializer.CommonUiTronDataDeserializer
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.rr.common.models.ItemListData
import com.navi.rr.milestones.models.MilestoneDataV2
import com.navi.rr.uitron.model.data.CountDownTextData
@@ -52,6 +53,9 @@ class RRComposeDataDeserializer : CommonUiTronDataDeserializer() {
RRComposeViewType.ReferralBottomSheetList.value -> {
context?.deserialize(jsonObject, ItemListData::class.java)
}
RRComposeViewType.NotifyWidget.value -> {
context?.deserialize(jsonObject, NotifyWidgetTextData::class.java)
}
else -> super.deserialize(json, typeOfT, context)
}
}

View File

@@ -14,6 +14,7 @@ import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.CustomSpannableProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.NotifyWidgetProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
import com.navi.rr.uitron.model.ui.TextWithShadowProperty
import com.navi.uitron.model.ui.BaseProperty
@@ -44,6 +45,9 @@ class RRComposePropertyDeserializer : CommonUiTronPropertyDeserializer() {
RRComposeViewType.CustomSpannableText.value -> {
context?.deserialize(jsonObject, CustomSpannableProperty::class.java)
}
RRComposeViewType.NotifyWidget.value -> {
context?.deserialize(jsonObject, NotifyWidgetProperty::class.java)
}
else -> super.deserialize(json, typeOfT, context)
}
}

View File

@@ -10,6 +10,7 @@ package com.navi.rr.common.serializer
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import com.navi.common.uitron.serializer.CommonUiTronDataSerializer
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.rr.common.models.ItemListData
import com.navi.rr.milestones.models.MilestoneDataV2
import com.navi.rr.uitron.model.data.CountDownTextData
@@ -52,6 +53,9 @@ class RRComposeDataSerializer : CommonUiTronDataSerializer() {
RRComposeViewType.CustomSpannableText.value -> {
context?.serialize(src as SpannableTextData, SpannableTextData::class.java)
}
RRComposeViewType.NotifyWidget.value -> {
context?.serialize(src as NotifyWidgetTextData, NotifyWidgetTextData::class.java)
}
else -> super.serialize(src, typeOfSrc, context)
}
}

View File

@@ -14,6 +14,7 @@ import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.CustomSpannableProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.NotifyWidgetProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
import com.navi.rr.uitron.model.ui.TextWithShadowProperty
import com.navi.uitron.model.ui.BaseProperty
@@ -50,6 +51,9 @@ class RRComposePropertySerializer : CommonUiTronPropertySerializer() {
RRComposeViewType.CustomSpannableText.value -> {
context?.serialize(src, CustomSpannableProperty::class.java)
}
RRComposeViewType.NotifyWidget.value -> {
context?.serialize(src, NotifyWidgetProperty::class.java)
}
else -> super.serialize(src, typeOfSrc, context)
}
}

View File

@@ -7,6 +7,14 @@
package com.navi.rr.common.widgetFactory
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
@@ -18,15 +26,31 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.gson.reflect.TypeToken
import com.navi.base.model.CtaData
import com.navi.base.sharedpref.PreferenceManager
import com.navi.common.alchemist.model.AlchemistWidgetModelDefinition
import com.navi.common.forge.model.WidgetModelDefinition
import com.navi.common.forge.model.WidgetTypes
import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.naviwidgets.utils.NOTIFY_WIDGET_DISMISSED_TIMESTAMP
import com.navi.naviwidgets.views.composables.NotifyPermissionBottomSheet
import com.navi.naviwidgets.views.composables.NotifyWidgetComposable
import com.navi.naviwidgets.views.composables.isNotifyWidgetVisible
import com.navi.rr.R
import com.navi.rr.common.models.ItemListData
import com.navi.rr.common.vm.RRBaseVM
import com.navi.rr.milestones.models.MilestoneBottomSheet
import com.navi.rr.milestones.models.MilestoneDataV2
import com.navi.rr.milestones.models.MilestoneTicketUrl
@@ -34,6 +58,13 @@ import com.navi.rr.milestones.ui.compose.MileStoneRenderer
import com.navi.rr.referral.models.ProgrammeVertical
import com.navi.rr.referral.ui.compose.ReferralSummaryRenderer
import com.navi.rr.uitron.render.RRCustomUiTronRenderer
import com.navi.rr.utils.constants.Constants.HIDE
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET
import com.navi.rr.utils.constants.Constants.NOTIFY_WIDGET_CLICKED
import com.navi.rr.utils.constants.Constants.OPEN_APP_SETTINGS
import com.navi.rr.utils.constants.Constants.PERMISSIONS_GIVEN
import com.navi.rr.utils.constants.Constants.PERMISSION_BOTTOMSHEET
import com.navi.rr.utils.constants.Constants.RR_BOTTOM_SHEET
import com.navi.rr.utils.constants.RefereeTrackerConstants.CONFIG_REPLICATOR_WIDGET
import com.navi.rr.utils.constants.RefereeTrackerConstants.LIST_ITEM
import com.navi.rr.utils.constants.RefereeTrackerConstants.REFERRAL_SUMMARY_WIDGET
@@ -85,10 +116,11 @@ fun WidgetRenderer(
}
}
CONFIG_REPLICATOR_WIDGET -> {
val widgetData = widget.widgetData?.data?.get(LIST_ITEM) as ItemListData
val widgetData = widget.widgetData?.data?.get(LIST_ITEM) as? ItemListData
val type = object : TypeToken<List<Map<String, Any?>>?>() {}.type
val list: List<Map<String, Any?>>? =
getGsonBuilders().fromJson(getGsonBuilders().toJson(widgetData.items), type)
getGsonBuilders()
.fromJson(getGsonBuilders().toJson(widgetData?.items), type)
Column() {
ReplicatorWidgetRenderer(
viewModel = viewModel,
@@ -97,6 +129,15 @@ fun WidgetRenderer(
)
}
}
NOTIFY_WIDGET -> {
val widgetData =
widget.widgetData?.data?.get("notifyWidgetTextData")
as? NotifyWidgetTextData
NotifyWidgetRenderer(viewModel = viewModel as RRBaseVM, widgetData)
}
PERMISSION_BOTTOMSHEET -> {
PermissionBottomSheetRenderer(viewModel = viewModel as RRBaseVM)
}
else -> {
val milestoneData =
widget.widgetData?.data?.get("milestoneData") as MilestoneDataV2
@@ -134,6 +175,20 @@ fun WidgetRenderer(
modifier = modifier
)
}
WidgetTypes.NATIVE_WIDGET.name -> {
when (widget.widgetName) {
NOTIFY_WIDGET -> {
val widgetData =
widget.widgetData?.data?.get("notifyWidgetTextData")
as? NotifyWidgetTextData
NotifyWidgetRenderer(viewModel = viewModel as RRBaseVM, widgetData)
}
PERMISSION_BOTTOMSHEET -> {
PermissionBottomSheetRenderer(viewModel = viewModel as RRBaseVM)
}
else -> {}
}
}
else -> Unit
}
}
@@ -195,3 +250,130 @@ fun ReplicatorWidgetRenderer(
}
referralWidgetList?.forEach { WidgetRenderer(widget = it, viewModel = viewModel) }
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NotifyWidgetRenderer(
viewModel: RRBaseVM,
data: NotifyWidgetTextData?,
color: Color = Color.White
) {
val notifyMeAnalytics: CommonNaviAnalytics.NotifyMe =
CommonNaviAnalytics.naviAnalytics.NotifyMe(viewModel.screenName)
var permissionRequested by remember { mutableStateOf(false) }
val notificationPermissionState =
rememberPermissionState(permission = android.Manifest.permission.POST_NOTIFICATIONS) {
permissionRequested = true
}
var isVisible by remember {
mutableStateOf(isNotifyWidgetVisible() && !notificationPermissionState.status.isGranted)
}
var isClickedState by remember { mutableStateOf(false) }
LaunchedEffect(notificationPermissionState.status.isGranted) {
if (notificationPermissionState.status.isGranted) {
isClickedState = true
val newCtaAction =
CtaAction(
ctaData =
CtaData(
url = PERMISSIONS_GIVEN,
)
)
viewModel.handleAction(newCtaAction)
}
}
LaunchedEffect(isVisible) {
if (isVisible) {
notifyMeAnalytics.notifyMeNudgeViewEvent()
}
}
AnimatedVisibility(
visible = isVisible,
enter =
expandVertically(
expandFrom = Alignment.Top,
clip = true,
animationSpec = geNotifyWidgetAnimationSpec()
) {
0
} + fadeIn(animationSpec = geNotifyWidgetAnimationSpec()),
exit =
shrinkVertically(
shrinkTowards = Alignment.Bottom,
clip = true,
animationSpec = geNotifyWidgetAnimationSpec()
) {
0
} + fadeOut(animationSpec = geNotifyWidgetAnimationSpec())
) {
NotifyWidgetComposable(
backgroundColor = color,
isClickedState = isClickedState,
isVisible = true,
data =
NotifyWidgetTextData(
mainText = data?.mainText,
subText = data?.subText,
successMainText = data?.successMainText,
successSubText = data?.successSubText,
ctaButtonText = data?.ctaButtonText,
spacerAbove = data?.spacerAbove,
spacerBelow = data?.spacerBelow,
),
onClick = {
val newCtaAction =
CtaAction(
ctaData =
CtaData(
url = NOTIFY_WIDGET_CLICKED,
)
)
viewModel.handleAction(newCtaAction)
},
onDismiss = {
PreferenceManager.setStringPreferenceApp(
NOTIFY_WIDGET_DISMISSED_TIMESTAMP,
System.currentTimeMillis().toString()
)
notifyMeAnalytics.notifyMeNudgeDismissEvent()
isVisible = false
}
)
}
}
@Composable
fun PermissionBottomSheetRenderer(
viewModel: RRBaseVM,
) {
NotifyPermissionBottomSheet(
titleText = stringResource(R.string.allow_notifications),
bodyTitleText = stringResource(R.string.app_notifications),
bodySubText = stringResource(R.string.to_enable),
bodySubSubText = stringResource(R.string.phone_settings),
buttonText = stringResource(R.string.go_to_settings),
onClick = {
viewModel.handleAction(
CtaAction(
ctaData =
CtaData(
url = OPEN_APP_SETTINGS,
)
)
)
viewModel.handleAction(
CtaAction(ctaData = CtaData(url = RR_BOTTOM_SHEET, action = HIDE))
)
},
onDismiss = {
viewModel.handleAction(
CtaAction(ctaData = CtaData(url = RR_BOTTOM_SHEET, action = HIDE))
)
}
)
}
fun <T> geNotifyWidgetAnimationSpec(): FiniteAnimationSpec<T> {
return tween(500, easing = CubicBezierEasing(0.83f, 0.17f, 0.23f, 0.89f))
}

View File

@@ -0,0 +1,12 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.rr.uitron.model.ui
import com.navi.uitron.model.ui.BaseProperty
class NotifyWidgetProperty() : BaseProperty()

View File

@@ -14,5 +14,6 @@ enum class RRComposeViewType(val value: String) {
Milestone("milestone"),
CountDownText("CountDownText"),
CustomSpannableText("CustomSpannableText"),
ReferralBottomSheetList("ReferralBottomSheetList")
ReferralBottomSheetList("ReferralBottomSheetList"),
NotifyWidget("NotifyWidget")
}

View File

@@ -0,0 +1,136 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.rr.uitron.render
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.navi.base.model.CtaData
import com.navi.common.uitron.model.action.CtaAction
import com.navi.elex.molecules.ElexNotifyWidget
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.naviwidgets.utils.CLOSE_WITH_FILL
import com.navi.naviwidgets.utils.NOTIFICATION_SUCCESS
import com.navi.naviwidgets.views.composables.isNotifyWidgetVisible
import com.navi.rr.common.vm.RRBaseVM
import com.navi.rr.common.widgetFactory.geNotifyWidgetAnimationSpec
import com.navi.rr.uitron.model.ui.NotifyWidgetProperty
import com.navi.rr.utils.constants.Constants.PERMISSIONS_GIVEN
import com.navi.uitron.model.data.UiTronData
import com.navi.uitron.render.Renderer
import com.navi.uitron.utils.hexToComposeColor
import com.navi.uitron.utils.orFalse
import com.navi.uitron.viewmodel.UiTronViewModel
class NotifyWidgetRenderer : Renderer<NotifyWidgetProperty> {
@OptIn(ExperimentalPermissionsApi::class)
@Composable
override fun Render(
property: NotifyWidgetProperty,
uiTronData: UiTronData?,
uiTronViewModel: UiTronViewModel,
modifier: Modifier?
) {
super.Render(property, uiTronData, uiTronViewModel, modifier)
var notifyWidgetData = uiTronData as? NotifyWidgetTextData
if (property.isStateFul.orFalse()) {
val state =
uiTronViewModel.handle
.getStateFlow<String?>(property.getPropertyId(), null)
.collectAsState()
property.copyNonNullFrom(property.statesMap?.get(state.value))
}
if (property.isDataMutable.orFalse()) {
val updatedDataState =
uiTronViewModel.handle
.getStateFlow<NotifyWidgetTextData?>(property.getDataId(), null)
.collectAsState()
notifyWidgetData = updatedDataState.value ?: notifyWidgetData
}
var permissionRequested by remember { mutableStateOf(false) }
val notificationPermissionState =
rememberPermissionState(permission = android.Manifest.permission.POST_NOTIFICATIONS) {
permissionRequested = true
}
var isVisible by remember {
mutableStateOf(isNotifyWidgetVisible() && !notificationPermissionState.status.isGranted)
}
var isClickedState by remember { mutableStateOf(false) }
LaunchedEffect(notificationPermissionState.status.isGranted) {
if (notificationPermissionState.status.isGranted) {
isClickedState = true
val newCtaAction =
CtaAction(
ctaData =
CtaData(
url = PERMISSIONS_GIVEN,
)
)
uiTronViewModel.handleAction(newCtaAction)
}
}
Row(modifier = Modifier.wrapContentSize()) {
AnimatedVisibility(
visible = property.visible == true && isVisible,
enter =
expandVertically(
expandFrom = Alignment.Top,
clip = true,
animationSpec = geNotifyWidgetAnimationSpec()
) {
0
} + fadeIn(animationSpec = geNotifyWidgetAnimationSpec()),
exit =
shrinkVertically(
shrinkTowards = Alignment.Bottom,
clip = true,
animationSpec = geNotifyWidgetAnimationSpec()
) {
0
} + fadeOut(animationSpec = geNotifyWidgetAnimationSpec())
) {
ElexNotifyWidget(
mainText = notifyWidgetData?.mainText.orEmpty(),
subText = notifyWidgetData?.subText.orEmpty(),
successMainText = notifyWidgetData?.successMainText.orEmpty(),
successSubText = notifyWidgetData?.successSubText.orEmpty(),
isSuccessState = isClickedState,
onClick = { uiTronViewModel.handleActions(notifyWidgetData?.onClick) },
ctaButtonText = notifyWidgetData?.ctaButtonText.orEmpty(),
notificationSuccessIconUrl =
notifyWidgetData?.notificationSuccessIconUrl ?: NOTIFICATION_SUCCESS,
crossIconUrl = notifyWidgetData?.crossIconUrl ?: CLOSE_WITH_FILL,
borderColor =
notifyWidgetData?.borderColor?.hexToComposeColor ?: Color(0xFFE3E5E5),
onDismiss = {
uiTronViewModel.handleActions(notifyWidgetData?.onDismiss)
(uiTronViewModel as RRBaseVM).handleActions(notifyWidgetData?.onDismiss)
}
)
}
}
}
}

View File

@@ -15,6 +15,7 @@ import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.CustomSpannableProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.NotifyWidgetProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
import com.navi.rr.uitron.model.ui.TextWithShadowProperty
import com.navi.rr.uitron.render.countdowntimer.CountDownTextRenderer
@@ -92,6 +93,17 @@ class RRCustomUiTronRenderer(parentScrollState: (() -> ScrollState)? = null) :
)
}
}
RRComposeViewType.NotifyWidget.name -> {
(composeView.property as? NotifyWidgetProperty)?.let {
NotifyWidgetRenderer()
.Render(
property = it,
uiTronData = dataMap?.getOrElse(it.layoutId.orEmpty()) { null },
uiTronViewModel = uiTronViewModel,
modifier
)
}
}
else -> {
super.Render(composeView, modifier, dataMap, uiTronViewModel)
}

View File

@@ -105,6 +105,21 @@ object Constants {
const val NAVI_COINS = "Navi coins"
const val NAVI_COIN = "Navi coin"
const val SCREEN_ID = "screenID"
const val NOTIFY_WIDGET_CLICKED = "NOTIFY_WIDGET_CLICKED"
const val PERMISSIONS_GIVEN = "PERMISSIONS_GIVEN"
const val NOTIFY_WIDGET_DISMISSED = "NOTIFY_WIDGET_DISMISSED"
const val ALLOW_NOTIFICATIONS = "Allow notifications"
const val APP_NOTIFICATIONS = "App notifications"
const val TO_ENABLE_NOTIFICATIONS = "To enable notifications"
const val PHONE_SETTINGS_APPS_NAVI_PERMISSIONS_NOTIFICATIONS =
"Phone settings > Apps > Navi > Permissions > notifications"
const val GO_TO_SETTINGS = "Go to settings"
const val OPEN_APP_SETTINGS = "OPEN_APP_SETTINGS"
const val NOTIFY_WIDGET = "NOTIFY_WIDGET"
const val PERMISSION_BOTTOMSHEET = "PERMISSION_BOTTOMSHEET"
const val SHOULD_NOTIFY_WIDGET = "showNotifyWidget"
const val RR_BOTTOM_SHEET = "RR_BOTTOM_SHEET"
const val HIDE = "HIDE"
}
object ContentProviderContractConstants {

View File

@@ -38,5 +38,10 @@
<string name="plus_amount">+%1$s</string>
<string name="tracker_expiry_left_days">" Days left"</string>
<string name="tracker_expiry_left_day">" Day left"</string>
<string name="allow_notifications">Allow notifications</string>
<string name="app_notifications">App notifications</string>
<string name="phone_settings">Phone settings > Apps > Navi > Permissions > notifications</string>
<string name="go_to_settings">Go to settings</string>
<string name="to_enable">To enable notifications</string>
</resources>

View File

@@ -0,0 +1,25 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.models
import com.navi.uitron.model.data.UiTronActionData
import com.navi.uitron.model.data.UiTronData
data class NotifyWidgetTextData(
val mainText: String? = null,
val subText: String? = null,
val successMainText: String? = null,
val successSubText: String? = null,
val ctaButtonText: String? = null,
val notificationSuccessIconUrl: String? = null,
val crossIconUrl: String? = null,
val spacerAbove: Int? = 0,
val spacerBelow: Int? = 0,
var onDismiss: UiTronActionData? = null,
var borderColor: String? = null,
) : UiTronData()

View File

@@ -32,6 +32,8 @@ const val VALIDATE_LOAN_MONTH = "VALIDATE_LOAN_MONTH"
const val FORWARD_SLASH = '/'
const val INVALID_INDEX = -1
const val INDIA_TIMEZONE = "UTC+05:30"
const val NOTIFY_WIDGET_DISMISSED_TIMESTAMP = "notify_widget_dismissed_timestamp"
const val NOTIFY_THRESHOLD_IN_MILLIS = "1296000000" // 15 days
// Chat widget constants
const val NAVI_CHAT_MESSAGE_WITH_ACTION_ITEM_SELECTED =

View File

@@ -0,0 +1,14 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.utils
const val NOTIFICATION_SUCCESS =
"https://public-assets.prod.navi-sa.in/navi-coin/png/Notification_success.png"
const val CLOSE_WITH_FILL =
"https://public-assets.prod.navi-sa.in/navi-coin/png/close_with_fill.png"
const val NAVI_LOGO = "https://public-assets.prod.navi-sa.in/navi-coin/png/filled_navi_logo.png"

View File

@@ -0,0 +1,124 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.views.composables
import androidx.compose.foundation.background
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.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.navi.elex.atoms.ElexAsyncImage
import com.navi.elex.atoms.ElexText
import com.navi.elex.font.FontWeightEnum
import com.navi.elex.molecules.ElexButtonWithText
import com.navi.naviwidgets.composewidget.reusable.colorCTAPrimary
import com.navi.naviwidgets.composewidget.reusable.colorTextPrimary
import com.navi.naviwidgets.composewidget.reusable.colorTextTertiary
import com.navi.naviwidgets.utils.NAVI_LOGO
@Composable
fun NotifyPermissionBottomSheet(
titleText: String,
bodyTitleText: String,
bodySubText: String,
bodySubSubText: String,
buttonText: String,
onClick: () -> Unit,
onDismiss: () -> Unit
) {
Column(
modifier =
Modifier.fillMaxWidth()
.wrapContentHeight()
.background(Color.White, shape = RoundedCornerShape(8.dp))
.padding(16.dp, 16.dp, 16.dp, 32.dp),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
ElexText(
text = titleText,
color = colorTextPrimary,
fontSize = 18.sp,
fontWeight = FontWeightEnum.NAVI_HEADLINE_REGULAR,
lineHeight = 22.sp
)
Row(
modifier =
Modifier.fillMaxWidth().wrapContentHeight().padding(0.dp, 24.dp, 0.dp, 32.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Top
) {
ElexAsyncImage(
icon = NAVI_LOGO,
contentDescription = "",
modifier = Modifier.height(40.dp).width(40.dp),
contentScale = ContentScale.FillBounds
)
Column(
modifier = Modifier.wrapContentSize().padding(8.dp, 0.dp, 0.dp, 0.dp),
verticalArrangement = Arrangement.Top
) {
ElexText(
text = bodyTitleText,
color = colorTextPrimary,
fontSize = 16.sp,
fontWeight = FontWeightEnum.NAVI_HEADLINE_REGULAR,
lineHeight = 22.sp
)
ElexText(
text = bodySubText,
color = colorTextTertiary,
fontSize = 12.sp,
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR
)
Spacer(Modifier.height(20.dp))
ElexText(
text = bodySubSubText,
color = colorTextTertiary,
fontSize = 12.sp,
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR,
textAlign = TextAlign.Start
)
}
}
ElexButtonWithText(
text = buttonText,
onClick = { onClick() },
modifier = Modifier.fillMaxWidth().height(48.dp),
textColor = Color.White,
fontSize = 14.sp,
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
enabled = true,
shape = RoundedCornerShape(4.dp),
colors =
ButtonDefaults.buttonColors(
contentColor = colorCTAPrimary,
containerColor = colorCTAPrimary,
),
border = null,
)
}
}

View File

@@ -0,0 +1,72 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.naviwidgets.views.composables
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.navi.base.sharedpref.PreferenceManager
import com.navi.elex.molecules.ElexNotifyWidget
import com.navi.naviwidgets.models.NotifyWidgetTextData
import com.navi.naviwidgets.utils.CLOSE_WITH_FILL
import com.navi.naviwidgets.utils.NOTIFICATION_SUCCESS
import com.navi.naviwidgets.utils.NOTIFY_THRESHOLD_IN_MILLIS
import com.navi.naviwidgets.utils.NOTIFY_WIDGET_DISMISSED_TIMESTAMP
import com.navi.uitron.utils.hexToComposeColor
@Composable
fun NotifyWidgetComposable(
backgroundColor: Color = Color.White,
isClickedState: Boolean,
isVisible: Boolean,
data: NotifyWidgetTextData?,
onClick: () -> Unit,
onDismiss: () -> Unit
) {
if (isVisible) {
Row(
modifier =
Modifier.background(backgroundColor)
.fillMaxWidth()
.wrapContentHeight()
.padding(
start = 16.dp,
end = 16.dp,
top = data?.spacerAbove?.dp ?: 0.dp,
bottom = data?.spacerBelow?.dp ?: 0.dp
)
) {
ElexNotifyWidget(
mainText = data?.mainText.orEmpty(),
subText = data?.subText.orEmpty(),
successMainText = data?.successMainText.orEmpty(),
successSubText = data?.successSubText.orEmpty(),
isSuccessState = isClickedState,
onClick = { onClick.invoke() },
ctaButtonText = data?.ctaButtonText.orEmpty(),
borderColor = data?.borderColor?.let { it.hexToComposeColor } ?: Color(0xFFE3E5E5),
notificationSuccessIconUrl =
data?.notificationSuccessIconUrl ?: NOTIFICATION_SUCCESS,
crossIconUrl = data?.crossIconUrl ?: CLOSE_WITH_FILL,
onDismiss = { onDismiss.invoke() }
)
}
}
}
fun isNotifyWidgetVisible(): Boolean {
val prevTime = PreferenceManager.getStringPreferenceApp(NOTIFY_WIDGET_DISMISSED_TIMESTAMP)
val difference = System.currentTimeMillis() - (prevTime?.toLong() ?: 0L)
return difference > NOTIFY_THRESHOLD_IN_MILLIS.toLong()
}