From 6983c5090b4fc208ba74597f8e55fbc24026aea6 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Tue, 30 Nov 2021 12:03:59 -0300 Subject: [PATCH] - Updating content seen at when playing podcast, watching video or reading newsletter articles - Using date to order chats on chat list --- .../detail/NewsletterDetailNavigatorImpl.kt | 5 ++-- .../primary/DashboardNavigatorImpl.kt | 4 +-- .../sphinx/concept_coredb/SphinxDatabase.sq | 5 ++++ .../concept_repository_chat/ChatRepository.kt | 2 ++ .../RepositoryMedia.kt | 4 +-- .../feature_repository/SphinxRepository.kt | 27 ++++++++++++++++++- .../navigation/NewsletterDetailNavigator.kt | 3 ++- .../ui/NewsletterDetailViewModel.kt | 1 + .../ui/watch/VideoFeedWatchScreenViewModel.kt | 6 +++++ .../web-view/web-view/build.gradle | 1 + .../web_view/navigation/ToWebViewDetail.kt | 9 ++++++- .../sphinx/web_view/ui/WebViewViewModel.kt | 26 +++++++++++++++++- .../res/navigation/web_view_nav_graph.xml | 4 +++ .../navigation/DashboardNavigator.kt | 2 +- .../dashboard/ui/adapter/DashboardChat.kt | 10 ++++++- .../ui/feed/read/FeedReadViewModel.kt | 6 +++-- .../MediaPlayerServiceControllerImpl.kt | 1 + .../service/MediaPlayerService.kt | 4 +++ 18 files changed, 106 insertions(+), 14 deletions(-) diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/NewsletterDetailNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/NewsletterDetailNavigatorImpl.kt index 1c9a62d404..ee820db329 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/NewsletterDetailNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/NewsletterDetailNavigatorImpl.kt @@ -3,6 +3,7 @@ package chat.sphinx.activitymain.navigation.navigators.detail import chat.sphinx.activitymain.navigation.drivers.DetailNavigationDriver import chat.sphinx.newsletter_detail.navigation.NewsletterDetailNavigator import chat.sphinx.web_view.navigation.ToWebViewDetail +import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.feed.FeedUrl import javax.inject.Inject @@ -14,9 +15,9 @@ internal class NewsletterDetailNavigatorImpl @Inject constructor( (navigationDriver as DetailNavigationDriver).closeDetailScreen() } - override suspend fun toWebViewDetail(title: String, url: FeedUrl) { + override suspend fun toWebViewDetail(chatId: ChatId?, title: String, url: FeedUrl) { detailDriver.submitNavigationRequest( - ToWebViewDetail(title, url, true) + ToWebViewDetail(chatId, title, url, true) ) } } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/primary/DashboardNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/primary/DashboardNavigatorImpl.kt index 9d470ffd6e..d6c3c2e985 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/primary/DashboardNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/primary/DashboardNavigatorImpl.kt @@ -60,9 +60,9 @@ internal class DashboardNavigatorImpl @Inject constructor( ) } - override suspend fun toWebViewDetail(title: String, url: FeedUrl) { + override suspend fun toWebViewDetail(chatId: ChatId?, title: String, url: FeedUrl) { detailDriver.submitNavigationRequest( - ToWebViewDetail(title, url, false) + ToWebViewDetail(chatId, title, url, false) ) } diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq index d8cc4ab3f6..49491051d7 100644 --- a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq @@ -106,6 +106,11 @@ UPDATE chatDbo SET is_muted = :muted WHERE id = ?; +chatUpdateContentSeenAt: +UPDATE chatDbo +SET content_seen_at = :content_seen_at +WHERE id = ?; + chatGetAll: SELECT * FROM chatDbo; diff --git a/sphinx/application/data/concepts/repositories/concept-repository-chat/src/main/java/chat/sphinx/concept_repository_chat/ChatRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-chat/src/main/java/chat/sphinx/concept_repository_chat/ChatRepository.kt index 69ac04835f..36303e8a63 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-chat/src/main/java/chat/sphinx/concept_repository_chat/ChatRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-chat/src/main/java/chat/sphinx/concept_repository_chat/ChatRepository.kt @@ -57,6 +57,8 @@ interface ChatRepository { * */ suspend fun toggleChatMuted(chat: Chat): Response + suspend fun updateChatContentSeenAt(chatId: ChatId) + fun joinTribe( tribeDto: TribeDto, ): Flow> diff --git a/sphinx/application/data/concepts/repositories/concept-repository-media/src/main/java/chat/sphinx/concept_repository_media/RepositoryMedia.kt b/sphinx/application/data/concepts/repositories/concept-repository-media/src/main/java/chat/sphinx/concept_repository_media/RepositoryMedia.kt index ac1bf884e2..ab1755b285 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-media/src/main/java/chat/sphinx/concept_repository_media/RepositoryMedia.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-media/src/main/java/chat/sphinx/concept_repository_media/RepositoryMedia.kt @@ -1,15 +1,15 @@ package chat.sphinx.concept_repository_media -import chat.sphinx.wrapper_chat.Chat import chat.sphinx.wrapper_chat.ChatMetaData import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_message.Message import chat.sphinx.wrapper_podcast.PodcastDestination -import kotlinx.coroutines.flow.Flow interface RepositoryMedia { fun updateChatMetaData(chatId: ChatId, metaData: ChatMetaData, shouldSync: Boolean = true) + suspend fun updateChatContentSeenAt(chatId: ChatId) + fun downloadMediaIfApplicable( message: Message, sent: Boolean, diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 32cc273c7f..92a8c42313 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -121,6 +121,9 @@ import okio.base64.encodeBase64 import java.io.File import java.io.InputStream import java.text.ParseException +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.LinkedHashMap import kotlin.math.absoluteValue @@ -3144,6 +3147,19 @@ abstract class SphinxRepository( return response } + override suspend fun updateChatContentSeenAt(chatId: ChatId) { + val queries = coreDB.getSphinxDatabaseQueries() + + chatLock.withLock { + withContext(io) { + queries.chatUpdateContentSeenAt( + DateTime(Date()), + chatId + ) + } + } + } + override fun joinTribe( tribeDto: TribeDto ): Flow> = flow { @@ -3410,6 +3426,7 @@ abstract class SphinxRepository( override fun getAllFeedsOfType(feedType: FeedType): Flow> = flow { val queries = coreDB.getSphinxDatabaseQueries() + emitAll( queries.feedGetAllByFeedType(feedType) .asFlow() @@ -3481,7 +3498,7 @@ abstract class SphinxRepository( } } - return listFeedDbo.map { + val list = listFeedDbo.map { mapFeedDbo( feedDbo = it, items = itemsMap[it.id] ?: listOf(), @@ -3490,6 +3507,14 @@ abstract class SphinxRepository( chat = chatsMap[it.chat_id] ) } + + var sortedList: List? = null + + withContext(dispatchers.default) { + sortedList = list.sortedByDescending { it.chat?.contentSeenAt?.time ?: it.lastItem?.datePublished?.time ?: 0 } + } + + return sortedList ?: listOf() } private suspend fun mapFeedDbo( diff --git a/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/navigation/NewsletterDetailNavigator.kt b/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/navigation/NewsletterDetailNavigator.kt index 5418997d74..3d1d820d85 100644 --- a/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/navigation/NewsletterDetailNavigator.kt +++ b/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/navigation/NewsletterDetailNavigator.kt @@ -1,6 +1,7 @@ package chat.sphinx.newsletter_detail.navigation import androidx.navigation.NavController +import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.feed.FeedUrl import io.matthewnelson.android_feature_navigation.requests.PopBackStack import io.matthewnelson.concept_navigation.BaseNavigationDriver @@ -12,7 +13,7 @@ abstract class NewsletterDetailNavigator( abstract suspend fun closeDetailScreen() - abstract suspend fun toWebViewDetail(title: String, url: FeedUrl) + abstract suspend fun toWebViewDetail(chatId: ChatId?, title: String, url: FeedUrl) suspend fun popBackStack() { navigationDriver.submitNavigationRequest(PopBackStack()) diff --git a/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/ui/NewsletterDetailViewModel.kt b/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/ui/NewsletterDetailViewModel.kt index bb50fcea51..a39eac6f04 100644 --- a/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/ui/NewsletterDetailViewModel.kt +++ b/sphinx/screens-detail/newsletter-detail/newsletter-detail/src/main/java/chat/sphinx/newsletter_detail/ui/NewsletterDetailViewModel.kt @@ -83,6 +83,7 @@ internal class NewsletterDetailViewModel @Inject constructor( fun newsletterItemSelected(item: FeedItem) { viewModelScope.launch(mainImmediate) { navigator.toWebViewDetail( + item?.feed?.chat?.id ?: item?.feed?.chatId, app.getString(R.string.newsletter_article), item.enclosureUrl ) diff --git a/sphinx/screens-detail/video-screen/video-screen/src/main/java/chat/sphinx/video_screen/ui/watch/VideoFeedWatchScreenViewModel.kt b/sphinx/screens-detail/video-screen/video-screen/src/main/java/chat/sphinx/video_screen/ui/watch/VideoFeedWatchScreenViewModel.kt index 2bb7c083be..f1d85f52a6 100644 --- a/sphinx/screens-detail/video-screen/video-screen/src/main/java/chat/sphinx/video_screen/ui/watch/VideoFeedWatchScreenViewModel.kt +++ b/sphinx/screens-detail/video-screen/video-screen/src/main/java/chat/sphinx/video_screen/ui/watch/VideoFeedWatchScreenViewModel.kt @@ -37,6 +37,12 @@ internal class VideoFeedWatchScreenViewModel @Inject constructor( init { subscribeToViewStateFlow() + + viewModelScope.launch(mainImmediate) { + chatRepository.updateChatContentSeenAt( + getArgChatId() + ) + } } open val playingVideoStateContainer: ViewStateContainer by lazy { diff --git a/sphinx/screens-detail/web-view/web-view/build.gradle b/sphinx/screens-detail/web-view/web-view/build.gradle index 2084eeb9d1..39db623b2f 100644 --- a/sphinx/screens-detail/web-view/web-view/build.gradle +++ b/sphinx/screens-detail/web-view/web-view/build.gradle @@ -38,6 +38,7 @@ dependencies { api project(path: ':sphinx:activity:insetter-activity') api project(path: ':sphinx:application:common:wrappers:wrapper-common') + implementation project(path: ':sphinx:application:data:concepts:repositories:concept-repository-chat') implementation project(path: ':sphinx:screens-detail:common:detail-resources') implementation project(path: ':sphinx:application:common:logger') diff --git a/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/navigation/ToWebViewDetail.kt b/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/navigation/ToWebViewDetail.kt index 87ac4b9395..0a4ee9bee2 100644 --- a/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/navigation/ToWebViewDetail.kt +++ b/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/navigation/ToWebViewDetail.kt @@ -4,11 +4,13 @@ import androidx.navigation.NavController import chat.sphinx.detail_resources.DetailNavOptions import chat.sphinx.web_view.R import chat.sphinx.web_view.ui.WebViewFragmentArgs +import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.feed.FeedUrl import io.matthewnelson.concept_navigation.NavigationRequest import io.matthewnelson.android_feature_navigation.R as nav_R class ToWebViewDetail( + private val chatId: ChatId?, private val title: String, private val url: FeedUrl, private val fromList: Boolean, @@ -26,7 +28,12 @@ class ToWebViewDetail( controller.navigate( R.id.web_view_nav_graph, - WebViewFragmentArgs.Builder(title, url.value, fromList) + WebViewFragmentArgs.Builder( + chatId?.value ?: ChatId.NULL_CHAT_ID.toLong(), + title, + url.value, + fromList + ) .build() .toBundle(), navOptions diff --git a/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/ui/WebViewViewModel.kt b/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/ui/WebViewViewModel.kt index d4e02252b7..d4e0b5468f 100644 --- a/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/ui/WebViewViewModel.kt +++ b/sphinx/screens-detail/web-view/web-view/src/main/java/chat/sphinx/web_view/ui/WebViewViewModel.kt @@ -1,15 +1,39 @@ package chat.sphinx.web_view.ui +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import chat.sphinx.concept_repository_chat.ChatRepository import chat.sphinx.web_view.navigation.WebViewNavigator +import chat.sphinx.wrapper_common.dashboard.ChatId import dagger.hilt.android.lifecycle.HiltViewModel +import io.matthewnelson.android_feature_navigation.util.navArgs import io.matthewnelson.android_feature_viewmodel.BaseViewModel import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import kotlinx.coroutines.launch import javax.inject.Inject +internal inline val WebViewFragmentArgs.chatId: ChatId? + get() = if (argChatId == ChatId.NULL_CHAT_ID.toLong()) { + null + } else { + ChatId(argChatId) + } + @HiltViewModel internal class WebViewViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, dispatchers: CoroutineDispatchers, - val navigator: WebViewNavigator + val navigator: WebViewNavigator, + private val chatRepository: ChatRepository, ): BaseViewModel(dispatchers, WebViewViewState.Idle) { + private val args: WebViewFragmentArgs by savedStateHandle.navArgs() + + init { + args.chatId?.let { chatId -> + viewModelScope.launch(mainImmediate) { + chatRepository.updateChatContentSeenAt(chatId) + } + } + } } diff --git a/sphinx/screens-detail/web-view/web-view/src/main/res/navigation/web_view_nav_graph.xml b/sphinx/screens-detail/web-view/web-view/src/main/res/navigation/web_view_nav_graph.xml index 0eec85dd1c..ea487eaba5 100644 --- a/sphinx/screens-detail/web-view/web-view/src/main/res/navigation/web_view_nav_graph.xml +++ b/sphinx/screens-detail/web-view/web-view/src/main/res/navigation/web_view_nav_graph.xml @@ -10,6 +10,10 @@ android:name="chat.sphinx.web_view.ui.WebViewFragment" tools:layout="@layout/fragment_web_view" > + + diff --git a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/navigation/DashboardNavigator.kt b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/navigation/DashboardNavigator.kt index cd852c3c11..db4ef93b6b 100644 --- a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/navigation/DashboardNavigator.kt +++ b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/navigation/DashboardNavigator.kt @@ -30,7 +30,7 @@ abstract class DashboardNavigator( abstract suspend fun toVideoWatchScreen(chatId: ChatId, feedUrl: FeedUrl) - abstract suspend fun toWebViewDetail(title: String, url: FeedUrl) + abstract suspend fun toWebViewDetail(chatId: ChatId?, title: String, url: FeedUrl) abstract suspend fun toNewsletterDetail(chatId: ChatId, feedUrl: FeedUrl) diff --git a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/adapter/DashboardChat.kt b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/adapter/DashboardChat.kt index 2c9c710e3d..128830ac06 100644 --- a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/adapter/DashboardChat.kt +++ b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/adapter/DashboardChat.kt @@ -55,7 +55,15 @@ sealed class DashboardChat { open val owner: Contact? = null override val sortBy: Long - get() = message?.date?.time ?: chat.createdAt.time + get() { + val lastContentSeenDate = chat.contentSeenAt?.time + val lastMessageActionDate = message?.date?.time ?: chat.createdAt.time + + if (lastContentSeenDate != null && lastContentSeenDate > lastMessageActionDate) { + return lastContentSeenDate + } + return lastMessageActionDate + } override fun getDisplayTime(today00: DateTime): String { return message?.date?.chatTimeFormat(today00) ?: "" diff --git a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/feed/read/FeedReadViewModel.kt b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/feed/read/FeedReadViewModel.kt index 0ad3073a1f..17a2c6be9d 100644 --- a/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/feed/read/FeedReadViewModel.kt +++ b/sphinx/screens/dashboard/dashboard/src/main/java/chat/sphinx/dashboard/ui/feed/read/FeedReadViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.Context import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import chat.sphinx.concept_repository_chat.ChatRepository import chat.sphinx.concept_repository_dashboard_android.RepositoryDashboardAndroid import chat.sphinx.dashboard.R import chat.sphinx.dashboard.navigation.DashboardNavigator @@ -48,11 +49,12 @@ class FeedReadViewModel @Inject constructor( } } - fun newsletterItemSelected(episode: FeedItem) { + fun newsletterItemSelected(item: FeedItem) { viewModelScope.launch(mainImmediate) { dashboardNavigator.toWebViewDetail( + item?.feed?.chat?.id ?: item?.feed?.chatId, app.getString(R.string.newsletter_article), - episode.enclosureUrl + item.enclosureUrl ) } } diff --git a/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/MediaPlayerServiceControllerImpl.kt b/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/MediaPlayerServiceControllerImpl.kt index 7f4129ad12..441bdab957 100644 --- a/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/MediaPlayerServiceControllerImpl.kt +++ b/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/MediaPlayerServiceControllerImpl.kt @@ -16,6 +16,7 @@ import chat.sphinx.feature_service_media_player_android.util.toIntent import io.matthewnelson.concept_coroutines.CoroutineDispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext diff --git a/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/service/MediaPlayerService.kt b/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/service/MediaPlayerService.kt index 3df7b68483..3830c02e71 100644 --- a/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/service/MediaPlayerService.kt +++ b/sphinx/service/features/media-player/feature-service-media-player-android/src/main/java/chat/sphinx/feature_service_media_player_android/service/MediaPlayerService.kt @@ -180,6 +180,10 @@ internal abstract class MediaPlayerService: SphinxService() { } is UserAction.ServiceAction.Play -> { + serviceLifecycleScope.launch { + repositoryMedia.updateChatContentSeenAt(userAction.chatId) + } + podData?.let { nnData -> if (nnData.chatId != userAction.chatId) { //Podcast has changed. Payments Destinations needs to be set again