Skip to content

Commit

Permalink
feat: Add notification deletion animation
Browse files Browse the repository at this point in the history
  • Loading branch information
vinaygregoryjohn183 committed Apr 19, 2024
1 parent 1c79e07 commit ec76243
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 86 deletions.
2 changes: 1 addition & 1 deletion siren-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")

// Coil
implementation("io.coil-kt:coil-compose:1.4.0")
implementation("io.coil-kt:coil-compose:2.6.0")
}

afterEvaluate {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.keyvalue.siren.androidsdk.core

import android.content.Context
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -65,6 +68,7 @@ import com.keyvalue.siren.androidsdk.utils.constants.TOKEN_VERIFICATION_RETRY_IN
import com.keyvalue.siren.androidsdk.utils.constants.TokenVerificationStatus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
Expand Down Expand Up @@ -132,7 +136,10 @@ abstract class SDKCoreUI(context: Context, userToken: String, recipientId: Strin
painter = painterResource(id = R.drawable.bell_dark),
contentDescription = "siren-notification-icon",
tint = if (props.darkMode == true) Color.White else Color.Black,
modifier = Modifier.size(iconStyle?.size!!).semantics { contentDescription = "siren-notification-icon" },
modifier =
Modifier
.size(iconStyle?.size!!)
.semantics { contentDescription = "siren-notification-icon" },
)
}

Expand Down Expand Up @@ -380,22 +387,26 @@ abstract class SDKCoreUI(context: Context, userToken: String, recipientId: Strin
}
}

var deletedItem by remember {
mutableStateOf("")
}

LaunchedEffect(deletedItem) {
if (deletedItem != "") {
delay(300)
deleteByIdState.emit(deletedItem)
deletedItem = ""
}
}

deleteByIdState.collectAsState().apply {
if (this.value.isNotEmpty()) {
var position: Int
notificationListState.mapIndexed { index, responseData ->
if (responseData?.id == this.value) {
position = index
notificationListState = notificationListState.subList(
0, if (position == 0) 0 else position,
) +
notificationListState.subList(
position + 1, notificationListState.size,
)
if (notificationListState.isEmpty()) {
showListEmptyState = true
}
notificationListState =
notificationListState.filter { item ->
item?.id != this.value
}
if (notificationListState.isEmpty()) {
showListEmptyState = true
}
}
}
Expand Down Expand Up @@ -594,51 +605,53 @@ abstract class SDKCoreUI(context: Context, userToken: String, recipientId: Strin
if (notificationData != null) {
it(notificationData)
}
} ?: NotificationCard(
notification = notificationData,
cardProps = props.cardProps,
notificationCardStyle,
onCardClick = {
callback.onCardClick(it)
},
deleteNotificationCallback = {
notificationData?.id?.let {
deleteNotificationInner(
it,
) { dataStatus, id, jsonObject, isError ->
CoroutineScope(Dispatchers.Main).launch {
if (isError && jsonObject != null) {
callback.onError(jsonObject)
} else {
deleteByIdState.emit(it)
} ?: androidx.compose.animation.AnimatedVisibility(visible = deletedItem != notificationData?.id, exit = shrinkVertically(animationSpec = tween(200)), enter = EnterTransition.None) {
NotificationCard(
notification = notificationData,
cardProps = props.cardProps,
notificationCardStyle,
onCardClick = {
callback.onCardClick(it)
},
deleteNotificationCallback = {
notificationData?.id?.let {
deleteNotificationInner(
it,
) { dataStatus, id, jsonObject, isError ->
CoroutineScope(Dispatchers.Main).launch {
if (isError && jsonObject != null) {
callback.onError(jsonObject)
} else {
deletedItem = (id ?: "")
}
}
}
}
}
},
themeColors = themeColors,
darkMode = props.darkMode ?: false,
defaultCardClickCallback = {
notificationData?.id?.let {
markAsReadInner(
it,
) {
responseData, errorObject, isError ->
CoroutineScope(Dispatchers.Main).launch {
if (isError && errorObject != null) {
callback.onError(errorObject)
} else {
responseData?.id?.let { id ->
markAsReadByIdState.emit(
id,
)
},
themeColors = themeColors,
darkMode = props.darkMode ?: false,
defaultCardClickCallback = {
notificationData?.id?.let {
markAsReadInner(
it,
) {
responseData, errorObject, isError ->
CoroutineScope(Dispatchers.Main).launch {
if (isError && errorObject != null) {
callback.onError(errorObject)
} else {
responseData?.id?.let { id ->
markAsReadByIdState.emit(
id,
)
}
}
}
}
}
}
},
)
},
)
}
}
}
item {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Spacer
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.shape.CircleShape
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.material.Icon
Expand All @@ -27,6 +26,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.contentDescription
Expand All @@ -37,8 +37,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
import coil.transform.CircleCropTransformation
import coil.compose.AsyncImage
import com.keyvalue.siren.androidsdk.R
import com.keyvalue.siren.androidsdk.data.model.AllNotificationResponseData
import com.keyvalue.siren.androidsdk.helper.client.CardProps
Expand All @@ -59,18 +58,23 @@ fun NotificationCard(
darkMode: Boolean,
) {
val avatarImageUrl = notification?.message?.avatar?.imageUrl
val avatarContentDescription = "siren-notification-avatar-${notification?.id}"
val avatarModifier =
Modifier
.size(notificationCardStyle?.avatarSize!!)
.clip(CircleShape)
.conditional(cardProps?.onAvatarClick != null && notification != null) {
clickable {
cardProps?.onAvatarClick?.let {
if (notification != null) {
it(notification)
}
}
}
}
.semantics { contentDescription = "siren-notification-avatar-${notification?.id}" }

val painter =
if (avatarImageUrl.isNullOrEmpty()) {
painterResource(id = if (darkMode) R.drawable.avatar_dark else R.drawable.avatar_light)
} else {
rememberImagePainter(
data = avatarImageUrl,
builder = {
transformations(CircleCropTransformation())
},
)
}
val avatarDefaultPainter = painterResource(id = if (darkMode) R.drawable.avatar_dark else R.drawable.avatar_light)

val modifier =
Modifier
Expand Down Expand Up @@ -118,25 +122,23 @@ fun NotificationCard(
horizontalArrangement = Arrangement.SpaceBetween,
) {
if (cardProps?.hideAvatar != true) {
Image(
painter = painter,
contentDescription = "siren-notification-avatar-${notification?.id}",
modifier =
Modifier
.size(notificationCardStyle.avatarSize!!)
.clip(CircleShape)
.weight(1f)
.conditional(cardProps?.onAvatarClick != null && notification != null) {
clickable {
cardProps?.onAvatarClick?.let {
if (notification != null) {
it(notification)
}
}
}
}
.semantics { contentDescription = "siren-notification-avatar-${notification?.id}" },
)
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) {
if (avatarImageUrl.isNullOrEmpty()) {
Image(
painter = avatarDefaultPainter,
contentDescription = avatarContentDescription,
contentScale = ContentScale.FillBounds,
modifier = avatarModifier,
)
} else {
AsyncImage(
model = avatarImageUrl,
contentDescription = avatarContentDescription,
contentScale = ContentScale.FillBounds,
modifier = avatarModifier,
)
}
}
}
Column(
modifier =
Expand Down

0 comments on commit ec76243

Please sign in to comment.