Skip to content

Commit

Permalink
Merge pull request #690 from stakwork/tt/feature/push-notification-ha…
Browse files Browse the repository at this point in the history
…ndling

Handling push notification to fix issues:
  • Loading branch information
tomastiminskas authored Dec 13, 2023
2 parents 2bb3078 + 77a6932 commit 7b5d776
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.os.Build
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
Expand All @@ -27,12 +26,12 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.chrisbanes.insetter.applyInsetter
import io.matthewnelson.android_feature_navigation.requests.PopBackStack
import io.matthewnelson.android_feature_viewmodel.updateViewState
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import chat.sphinx.resources.R as R_common


@AndroidEntryPoint
internal class MainActivity: MotionLayoutNavigationActivity<
class MainActivity: MotionLayoutNavigationActivity<
MainViewState,
MainViewModel,
PrimaryNavigationDriver,
Expand Down Expand Up @@ -72,6 +71,8 @@ internal class MainActivity: MotionLayoutNavigationActivity<
private var statusBarInsets: InsetPadding? = null
private var navigationBarInsets: InsetPadding? = null
private var keyboardInsets: InsetPadding? = null

var isActive = false
}

override val statusBarInsetHeight: InsetPadding by lazy(LazyThreadSafetyMode.NONE) {
Expand Down Expand Up @@ -122,6 +123,10 @@ internal class MainActivity: MotionLayoutNavigationActivity<
binding.viewMainInputLock.setOnClickListener { viewModel }
askNotificationPermission()
addWindowInsetChangeListener()

intent.extras?.getString("chat_id")?.toLongOrNull()?.let { chatId ->
handlePushNotification(chatId)
}
}

override fun onStart() {
Expand Down Expand Up @@ -165,7 +170,11 @@ internal class MainActivity: MotionLayoutNavigationActivity<
}
}

override fun onResume() {
super.onResume()

isActive = true
}

override fun onStop() {
super.onStop()
Expand All @@ -175,6 +184,8 @@ internal class MainActivity: MotionLayoutNavigationActivity<
if (sessionDepth == 0) {
viewModel.saveContentFeedStatuses()
}

isActive = false
}

override fun onNewIntent(intent: Intent) {
Expand All @@ -183,6 +194,8 @@ internal class MainActivity: MotionLayoutNavigationActivity<
intent.dataString?.let { deepLink ->
handleDeepLink(deepLink)
}

setIntent(intent)
}

private fun askNotificationPermission() {
Expand All @@ -203,12 +216,19 @@ internal class MainActivity: MotionLayoutNavigationActivity<
}
}
}

private fun handleDeepLink(deepLink: String) {
onStopSupervisor.scope.launch(viewModel.mainImmediate) {
viewModel.handleDeepLink(deepLink)
}
}

private fun handlePushNotification(chatId: Long) {
onStopSupervisor.scope.launch(viewModel.mainImmediate) {
viewModel.handlePushNotification(chatId)
}
}

override suspend fun onViewStateFlowCollect(viewState: MainViewState) {
when (viewState) {
is MainViewState.DetailScreenActive -> {
Expand Down Expand Up @@ -237,16 +257,16 @@ internal class MainActivity: MotionLayoutNavigationActivity<
// To Handle swipe behaviour
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
transitionInProgress = false
if (
currentId == MainViewState.DetailScreenInactive.endSetId &&
detailNavController.previousBackStackEntry != null
) {
lifecycleScope.launch {
viewModel.detailDriver.submitNavigationRequest(
PopBackStack(R.id.navigation_detail_blank_fragment)
)
}
if (
currentId == MainViewState.DetailScreenInactive.endSetId &&
detailNavController.previousBackStackEntry != null
) {
lifecycleScope.launch {
viewModel.detailDriver.submitNavigationRequest(
PopBackStack(R.id.navigation_detail_blank_fragment)
)
}
}
}

override fun onBackPressed() {
Expand Down Expand Up @@ -295,6 +315,8 @@ internal class MainActivity: MotionLayoutNavigationActivity<

viewModel.syncActions()
viewModel.getDeleteExcessFileIfApplicable()

isActive = false
}

override var isKeyboardVisible: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import chat.sphinx.wrapper_common.StorageData
import chat.sphinx.wrapper_common.StorageLimit.DEFAULT_STORAGE_LIMIT
import chat.sphinx.wrapper_common.StorageLimit.STORAGE_LIMIT_KEY
import chat.sphinx.wrapper_common.calculateUserStorageLimit
import chat.sphinx.wrapper_common.chat.PushNotificationLink
import chat.sphinx.wrapper_common.toFileSize
import dagger.hilt.android.lifecycle.HiltViewModel
import io.matthewnelson.android_feature_activity.NavigationViewModel
Expand All @@ -36,7 +37,7 @@ import kotlinx.coroutines.flow.collect
import javax.inject.Inject

@HiltViewModel
internal class MainViewModel @Inject constructor(
class MainViewModel @Inject constructor(
private val authenticationCoordinator: AuthenticationCoordinator,
private val authenticationStateManager: AuthenticationStateManager,
val authenticationDriver: AuthenticationNavigationDriver,
Expand Down Expand Up @@ -88,6 +89,20 @@ internal class MainViewModel @Inject constructor(
}
}

suspend fun handlePushNotification(chatId: Long) {
val pushNotificationLink = PushNotificationLink("sphinx.chat://?action=push&chatId=$chatId")

if (authenticationStateManager.authenticationStateFlow.value == AuthenticationState.NotRequired) {
navigationDriver.submitNavigationRequest(
ToDashboardScreen(
popUpToId = R.id.main_primary_nav_graph,
updateBackgroundLoginTime = false,
deepLink = pushNotificationLink.value
)
)
}
}

fun syncActions() {
actionsRepository.syncActions()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import androidx.navigation.NavController
import io.matthewnelson.concept_navigation.NavigationRequest
import io.matthewnelson.feature_navigation.NavigationDriver

internal class AuthenticationNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 1) {
class AuthenticationNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 1) {
override suspend fun whenTrueExecuteRequest(request: NavigationRequest<NavController>): Boolean {
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.matthewnelson.android_feature_navigation.requests.PopBackStack
import io.matthewnelson.concept_navigation.NavigationRequest
import io.matthewnelson.feature_navigation.NavigationDriver

internal class DetailNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 2) {
class DetailNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 2) {
override suspend fun whenTrueExecuteRequest(request: NavigationRequest<NavController>): Boolean {
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import androidx.navigation.NavController
import io.matthewnelson.concept_navigation.NavigationRequest
import io.matthewnelson.feature_navigation.NavigationDriver

internal class PrimaryNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 5) {
class PrimaryNavigationDriver: NavigationDriver<NavController>(replayCacheSize = 5) {
override suspend fun whenTrueExecuteRequest(request: NavigationRequest<NavController>): Boolean {
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import chat.sphinx.activitymain.R
import io.matthewnelson.android_concept_views.MotionLayoutViewState

@Suppress("ClassName")
internal sealed class MainViewState: MotionLayoutViewState<MainViewState>() {
sealed class MainViewState: MotionLayoutViewState<MainViewState>() {

object DetailScreenActive: MainViewState() {
override val startSetId: Int
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package chat.sphinx.wrapper_common.chat

@Suppress("NOTHING_TO_INLINE")
inline fun String.toPushNotificationLink(): PushNotificationLink? =
try {
PushNotificationLink(this)
} catch (e: IllegalArgumentException) {
null
}

inline val String.isValidPushNotificationLink: Boolean
get() = isNotEmpty() && matches("^${PushNotificationLink.REGEX}\$".toRegex())

@JvmInline
value class PushNotificationLink(val value: String) {

companion object {
const val REGEX = "sphinx\\.chat:\\/\\/\\?action=push&chatId=.*"
const val CHAT_ID = "chatId"
}

init {
require(value.isValidPushNotificationLink) {
"Invalid Push Notification Link"
}
}

inline val chatId : Long?
get() = (getComponent(CHAT_ID) ?: "").trim().toLongOrNull()

fun getComponent(k: String): String? {
val components = value.replace("sphinx.chat://", "").split("&")
for (component in components) {
val subComponents = component.split("=")
val key:String? = if (subComponents.isNotEmpty()) subComponents.elementAtOrNull(0) else null
val value:String? = if (subComponents.size > 1) subComponents.elementAtOrNull(1) else null

if (key == k) return value
}
return null
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import chat.sphinx.menu_bottom.ui.MenuBottomViewState
import chat.sphinx.resources.*
import chat.sphinx.wrapper_common.FileSize
import chat.sphinx.wrapper_common.asFormattedString
import chat.sphinx.wrapper_common.chat.PushNotificationLink
import chat.sphinx.wrapper_common.lightning.asFormattedString
import chat.sphinx.wrapper_common.lightning.toSat
import chat.sphinx.wrapper_common.message.MessageId
Expand Down Expand Up @@ -212,6 +213,22 @@ abstract class ChatFragment<
viewModel.screenInit()
}

override fun onResume() {
super.onResume()

handlePushNotification()
}

private fun handlePushNotification() {
val chatId = activity?.intent?.extras?.getString("chat_id")?.toLongOrNull() ?: activity?.intent?.extras?.getLong("chat_id")

chatId?.let { _ ->
lifecycleScope.launch(viewModel.mainImmediate) {
viewModel.chatNavigator.popBackStack()
}
}
}

override fun onKeyboardToggle() {
addViewKeyboardBottomPadding(
(requireActivity() as InsetterActivity)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package chat.sphinx.dashboard.ui

import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
Expand All @@ -10,7 +9,6 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat.setBackgroundTintList
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
Expand All @@ -28,18 +26,17 @@ import chat.sphinx.concept_signer_manager.SignerManager
import chat.sphinx.dashboard.R
import chat.sphinx.dashboard.databinding.FragmentDashboardBinding
import chat.sphinx.dashboard.ui.viewstates.*
import chat.sphinx.dashboard.ui.viewstates.DashboardMotionViewState
import chat.sphinx.insetter_activity.InsetterActivity
import chat.sphinx.insetter_activity.addNavigationBarPadding
import chat.sphinx.insetter_activity.addStatusBarPadding
import chat.sphinx.kotlin_response.LoadResponse
import chat.sphinx.kotlin_response.Response
import chat.sphinx.menu_bottom.databinding.LayoutMenuBottomBinding
import chat.sphinx.menu_bottom_scanner.BottomScannerMenu
import chat.sphinx.resources.databinding.LayoutPodcastPlayerFooterBinding
import chat.sphinx.wrapper_view.Px
import chat.sphinx.swipe_reveal_layout.SwipeRevealLayout
import chat.sphinx.wrapper_common.chat.PushNotificationLink
import chat.sphinx.wrapper_common.lightning.*
import chat.sphinx.wrapper_view.Px
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.matthewnelson.android_feature_screens.ui.motionlayout.MotionLayoutFragment
Expand All @@ -50,7 +47,6 @@ import io.matthewnelson.concept_views.viewstate.collect
import io.matthewnelson.concept_views.viewstate.value
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand Down Expand Up @@ -117,12 +113,30 @@ internal class DashboardFragment : MotionLayoutFragment<

viewModel.networkRefresh(false)

handleDeepLinks()
handlePushNotification()

activity?.intent = null
}

private fun handleDeepLinks() {
activity?.intent?.dataString?.let { deepLink ->
viewModel.handleDeepLink(deepLink)
activity?.intent?.data = null
}
}

private fun handlePushNotification() {
val chatId = activity?.intent?.extras?.getString("chat_id")?.toLongOrNull() ?: activity?.intent?.extras?.getLong("chat_id")
chatId?.let { nnChatId ->
viewModel.handleDeepLink(
PushNotificationLink("sphinx.chat://?action=push&chatId=$nnChatId").value
)
}

activity?.intent = null
}

override fun onRefresh() {
binding.swipeRefreshLayoutDataReload.isRefreshing = false
viewModel.networkRefresh(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,14 @@ import chat.sphinx.scanner_view_model_coordinator.request.ScannerFilter
import chat.sphinx.scanner_view_model_coordinator.request.ScannerRequest
import chat.sphinx.scanner_view_model_coordinator.response.ScannerResponse
import chat.sphinx.wrapper_chat.Chat
import chat.sphinx.wrapper_chat.isConversation
import chat.sphinx.wrapper_common.*
import chat.sphinx.wrapper_common.chat.ChatUUID
import chat.sphinx.wrapper_common.chat.PushNotificationLink
import chat.sphinx.wrapper_common.chat.toPushNotificationLink
import chat.sphinx.wrapper_common.dashboard.ChatId
import chat.sphinx.wrapper_common.dashboard.RestoreProgressViewState
import chat.sphinx.wrapper_common.dashboard.toChatId
import chat.sphinx.wrapper_common.feed.*
import chat.sphinx.wrapper_common.lightning.*
import chat.sphinx.wrapper_common.tribe.TribeJoinLink
Expand Down Expand Up @@ -189,6 +193,24 @@ internal class DashboardViewModel @Inject constructor(
handleRedeemSatsLink(redeemSatsLink)
} ?: deepLink?.toFeedItemLink()?.let { feedItemLink ->
handleFeedItemLink(feedItemLink)
} ?: deepLink?.toPushNotificationLink()?.let { pushNotificationLink ->
handlePushNotification(pushNotificationLink)
}
}
}

private fun handlePushNotification(pushNotificationLink: PushNotificationLink) {
viewModelScope.launch(mainImmediate) {
pushNotificationLink.chatId?.toChatId()?.let { nnChatId ->
chatRepository.getChatById(nnChatId).firstOrNull()?.let { chat ->
if (chat.isConversation()) {
chat.contactIds.elementAtOrNull(1)?.let { contactId ->
dashboardNavigator.toChatContact(nnChatId, contactId)
}
} else {
dashboardNavigator.toChatTribe(nnChatId)
}
}
}
}
}
Expand Down
Loading

0 comments on commit 7b5d776

Please sign in to comment.