Skip to content

Commit

Permalink
feat(class, notification): tham gia class thông qua notification
Browse files Browse the repository at this point in the history
  • Loading branch information
nqmgaming committed Nov 30, 2024
1 parent 81afb21 commit 10f67fb
Show file tree
Hide file tree
Showing 27 changed files with 134 additions and 75 deletions.
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ dependencies {
ksp(libs.androidx.room.compiler)
implementation(libs.compose.charts)


implementation(libs.play.services.ads)

implementation(project(":compose-cardstack"))
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/pwhs/quickmem/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.onesignal.OneSignal
import com.onesignal.debug.LogLevel as OneSignalLogLevel
import com.pwhs.quickmem.core.datastore.AppManager
import com.pwhs.quickmem.core.datastore.TokenManager
import com.pwhs.quickmem.data.dto.notification.TokenRequestDto
import com.pwhs.quickmem.data.dto.notification.DeviceTokenRequestDto
import com.pwhs.quickmem.data.remote.ApiService
import com.revenuecat.purchases.LogLevel as RevenueCatLogLevel
import com.revenuecat.purchases.Purchases
Expand Down Expand Up @@ -78,7 +78,7 @@ class App : Application() {
val accessToken = tokenManager.accessToken.firstOrNull() ?: ""
val userId = appManager.userId.firstOrNull() ?: ""
try {
apiService.sendDeviceToken(accessToken, TokenRequestDto(userId, token))
apiService.sendDeviceToken(accessToken, DeviceTokenRequestDto(userId, token))
Timber.d("Token sent to server successfully.")
} catch (e: Exception) {
Timber.e(e, "Error sending token to server")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.pwhs.quickmem.core.data.enums

enum class NotificationType(val type: String) {
INVITE_USER_JOIN_CLASS("INVITE_USER_JOIN_CLASS"),
NONE("NONE"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import androidx.core.app.NotificationCompat
import androidx.core.net.toUri
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.pwhs.quickmem.MainActivity
import com.pwhs.quickmem.R
import com.pwhs.quickmem.core.datastore.AppManager
import com.pwhs.quickmem.core.datastore.TokenManager
import com.pwhs.quickmem.data.dto.notification.TokenRequestDto
import com.pwhs.quickmem.data.dto.notification.DeviceTokenRequestDto
import com.pwhs.quickmem.data.remote.ApiService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -42,7 +43,7 @@ class AppFirebaseMessagingService : FirebaseMessagingService() {
val accessToken = tokenManager.accessToken.firstOrNull() ?: ""
val userId = appManager.userId.firstOrNull() ?: ""
try {
apiService.sendDeviceToken(accessToken, TokenRequestDto(userId, token))
apiService.sendDeviceToken(accessToken, DeviceTokenRequestDto(userId, token))
Timber.d("Token sent to server successfully.")
} catch (e: Exception) {
Timber.e(e, "Error sending token to server")
Expand All @@ -54,23 +55,38 @@ class AppFirebaseMessagingService : FirebaseMessagingService() {

override fun onMessageReceived(remoteMessage: RemoteMessage) {
Timber.d("From: ${remoteMessage.from}")
remoteMessage.notification?.title?.let { Timber.d("Notification Title: $it") }
remoteMessage.notification?.body?.let { Timber.d("Notification Body: $it") }
remoteMessage.data.isNotEmpty()
.let { Timber.d("Message data payload: ${remoteMessage.data}") }
CoroutineScope(Dispatchers.IO).launch {
val isPushNotificationsEnabled = appManager.pushNotifications.firstOrNull() ?: false
if (isPushNotificationsEnabled) {
remoteMessage.notification?.let {
showNotification(it.title, it.body)
showNotification(it.title, it.body, remoteMessage.data)
}
}
}
}

private fun showNotification(title: String?, body: String?) {
private fun showNotification(title: String?, body: String?, data: Map<String, String>) {
val channelId = "QuickMem Channel"
val notificationId = 0

val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val notificationType = data["notificationType"]
val classCode = data["code"]

val intent = if (notificationType == "INVITE_USER_JOIN_CLASS" && classCode != null) {
Intent(
Intent.ACTION_VIEW,
"quickmem://join/class?code=$classCode".toUri()
)
} else {
Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
}

val pendingIntent = PendingIntent.getActivity(
this,
0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.pwhs.quickmem.data.dto.notification

import com.google.gson.annotations.SerializedName

data class TokenRequestDto(
data class DeviceTokenRequestDto(
@SerializedName("userId")
val userId: String,
@SerializedName("deviceToken")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pwhs.quickmem.data.dto.notification

import com.google.gson.annotations.SerializedName
import com.pwhs.quickmem.core.data.enums.NotificationType

data class GetNotificationResponseDto(
@SerializedName("id")
Expand All @@ -13,6 +14,10 @@ data class GetNotificationResponseDto(
val userId: String,
@SerializedName("isRead")
val isRead: Boolean,
@SerializedName("notificationType")
val notificationType: NotificationType? = NotificationType.NONE,
@SerializedName("data")
val data: NotificationDataDto? = null,
@SerializedName("createdAt")
val createdAt: String,
@SerializedName("updatedAt")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.pwhs.quickmem.data.dto.notification

import com.google.gson.annotations.SerializedName

data class NotificationDataDto(
@SerializedName("id")
val id: String? = null,
@SerializedName("code")
val code: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ fun GetNotificationResponseDto.toModel() = GetNotificationResponseModel(
userId = userId,
isRead = isRead,
createdAt = createdAt,
updatedAt = updatedAt
updatedAt = updatedAt,
notificationType = notificationType,
data = data?.toModel()
)

fun GetNotificationResponseModel.toDto() = GetNotificationResponseDto(
Expand All @@ -20,5 +22,7 @@ fun GetNotificationResponseModel.toDto() = GetNotificationResponseDto(
userId = userId,
isRead = isRead,
createdAt = createdAt,
updatedAt = updatedAt
updatedAt = updatedAt,
notificationType = notificationType,
data = data?.toDto()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.pwhs.quickmem.data.mapper.notification

import com.pwhs.quickmem.data.dto.notification.NotificationDataDto
import com.pwhs.quickmem.domain.model.notification.NotificationDataModel

fun NotificationDataDto.toModel() = NotificationDataModel(
id = id,
code = code
)

fun NotificationDataModel.toDto() = NotificationDataDto(
id = id,
code = code
)
4 changes: 2 additions & 2 deletions app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import com.pwhs.quickmem.data.dto.folder.UpdateFolderRequestDto
import com.pwhs.quickmem.data.dto.folder.UpdateFolderResponseDto
import com.pwhs.quickmem.data.dto.notification.GetNotificationResponseDto
import com.pwhs.quickmem.data.dto.notification.MarkNotificationReadRequestDto
import com.pwhs.quickmem.data.dto.notification.TokenRequestDto
import com.pwhs.quickmem.data.dto.notification.DeviceTokenRequestDto
import com.pwhs.quickmem.data.dto.flashcard.WriteStatusFlashCardDto
import com.pwhs.quickmem.data.dto.streak.GetStreakDto
import com.pwhs.quickmem.data.dto.streak.GetTopStreakResponseDto
Expand Down Expand Up @@ -564,7 +564,7 @@ interface ApiService {
@POST("notifications/register")
suspend fun sendDeviceToken(
@Header("Authorization") authorization: String,
@Body tokenRequest: TokenRequestDto
@Body tokenRequest: DeviceTokenRequestDto
): Response<Unit>

@GET("notifications/user/{id}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.pwhs.quickmem.domain.model.notification

import com.pwhs.quickmem.core.data.enums.NotificationType

data class GetNotificationResponseModel(
val id: String,
val title: String,
val message: String,
val userId: String,
val notificationType: NotificationType? = NotificationType.NONE,
val data: NotificationDataModel? = null,
val isRead: Boolean,
val createdAt: String,
val updatedAt: String
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.pwhs.quickmem.domain.model.notification

data class NotificationDataModel(
val id: String? = null,
val code: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.pwhs.quickmem.presentation.app.deeplink

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.annotation.parameters.DeepLink
Expand All @@ -11,10 +10,7 @@ import com.ramcosta.composedestinations.generated.destinations.JoinClassScreenDe
import com.ramcosta.composedestinations.generated.destinations.LoadFolderScreenDestination
import com.ramcosta.composedestinations.generated.destinations.LoadStudySetScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import timber.log.Timber

//Deeplink for receiving code from class, folder, study set
//TODO: Implement this screen
@Destination<RootGraph>(
deepLinks = [
DeepLink(uriPattern = "quickmem://join/class?code={classCode}"),
Expand All @@ -24,20 +20,18 @@ import timber.log.Timber
)
@Composable
fun DeepLinkScreen(
modifier: Modifier = Modifier,
studySetCode: String? = null,
folderCode: String? = null,
classCode: String? = null,
navigator: DestinationsNavigator,
) {
Timber.d("DeepLinkScreen: studySetCode: $studySetCode, folderCode: $folderCode, classCode: $classCode")
LaunchedEffect(key1 = true) {
when {
classCode != null -> {
navigator.navigate(
JoinClassScreenDestination(
code = classCode,
type = "class"
isFromDeepLink = true
)
) {
popUpTo(NavGraphs.root) {
Expand All @@ -51,7 +45,6 @@ fun DeepLinkScreen(
folderCode != null -> {
navigator.navigate(
LoadFolderScreenDestination(
type = "folder",
folderCode = folderCode
)
) {
Expand All @@ -66,7 +59,6 @@ fun DeepLinkScreen(
studySetCode != null -> {
navigator.navigate(
LoadStudySetScreenDestination(
type = "studySet",
studySetCode = studySetCode
)
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pwhs.quickmem.presentation.app.deeplink.classes


data class JoinClassArgs(
val code: String,
val type: String
val isFromDeepLink: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
Expand Down Expand Up @@ -69,18 +69,23 @@ fun JoinClassScreen(
viewModel.uiEvent.collect { event ->
when (event) {
is JoinClassUiEvent.JoinedClass -> {
if (!uiState.isFromDeepLink) {
navigator.navigateUp()
}
navigator.navigate(
ClassDetailScreenDestination(
id = event.id,
title = event.title,
description = event.description
)
) {
popUpTo(NavGraphs.root) {
saveState = false
if (uiState.isFromDeepLink) {
popUpTo(NavGraphs.root) {
saveState = false
}
launchSingleTop = true
restoreState = false
}
launchSingleTop = true
restoreState = false
}
}

Expand Down Expand Up @@ -156,7 +161,7 @@ fun JoinClass(
onClick = onBackHome
) {
Icon(
imageVector = Icons.Default.Home,
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = Color.Gray.copy(alpha = 0.6f)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ data class JoinClassUiState(
val classId: String? = null,
val isUnAuthorized: Boolean = false,
val code: String? = null,
val type: String? = null,
val isFromDeepLink: Boolean = false,
val classDetailResponseModel: GetClassDetailResponseModel? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ class JoinClassViewModel @Inject constructor(
val uiEvent = _uiEvent.receiveAsFlow()

init {
val code = savedStateHandle.get<String>("code")
val type = savedStateHandle.get<String>("type")

Timber.d("JoinClassViewModel: code: $code, type: $type")

val code = savedStateHandle.get<String>("code") ?: ""
val isFromDeepLink = savedStateHandle.get<Boolean>("isFromDeepLink") ?: false
_uiState.update {
it.copy(
code = code,
type = type
isFromDeepLink = isFromDeepLink
)
}

Expand Down Expand Up @@ -85,6 +82,14 @@ class JoinClassViewModel @Inject constructor(

is Resources.Success -> {
if (resource.data?.owner?.id == userId || resource.data?.isJoined == true) {
_uiState.update {
it.copy(
classDetailResponseModel = resource.data,
isLoading = false,
userId = userId,
classId = resource.data.id
)
}
_uiEvent.send(
JoinClassUiEvent.JoinedClass(
id = resource.data.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.pwhs.quickmem.presentation.app.deeplink.folder


data class LoadFolderArgs(
val folderCode: String,
val type: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.pwhs.quickmem.presentation.app.deeplink.folder

data class LoadFolderUiState(
val folderCode: String = "",
val type: String = "",
val isLoading: Boolean = true,
val folderId: String? = null
)
Loading

0 comments on commit 10f67fb

Please sign in to comment.