Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • circles/circles-android
1 result
Show changes
Commits on Source (2)
Showing
with 37 additions and 174 deletions
...@@ -56,6 +56,13 @@ ...@@ -56,6 +56,13 @@
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="circles" android:host="app" android:pathPattern="/dmTimeline/.*" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name=".feature.share.circle.ShareWithCircleActivity" android:name=".feature.share.circle.ShareWithCircleActivity"
......
...@@ -3,10 +3,10 @@ package org.futo.circles.feature.direct.create.list ...@@ -3,10 +3,10 @@ package org.futo.circles.feature.direct.create.list
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.futo.circles.core.base.list.ViewBindingHolder import org.futo.circles.core.base.list.ViewBindingHolder
import org.futo.circles.core.databinding.ListItemCreateDmBinding
import org.futo.circles.core.extensions.loadUserProfileIcon import org.futo.circles.core.extensions.loadUserProfileIcon
import org.futo.circles.core.extensions.onClick import org.futo.circles.core.extensions.onClick
import org.futo.circles.core.model.CirclesUserSummary import org.futo.circles.core.model.CirclesUserSummary
import org.futo.circles.databinding.ListItemCreateDmBinding
class CreateDMUserViewHolder( class CreateDMUserViewHolder(
parent: ViewGroup, parent: ViewGroup,
......
package org.futo.circles.core.feature.direct package org.futo.circles.feature.direct.timeline
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
...@@ -11,13 +11,14 @@ import org.futo.circles.core.databinding.DialogFragmentDmTimelineBinding ...@@ -11,13 +11,14 @@ import org.futo.circles.core.databinding.DialogFragmentDmTimelineBinding
import org.futo.circles.core.extensions.dpToPx import org.futo.circles.core.extensions.dpToPx
import org.futo.circles.core.extensions.observeData import org.futo.circles.core.extensions.observeData
import org.futo.circles.core.mapping.nameOrId import org.futo.circles.core.mapping.nameOrId
import org.futo.circles.feature.timeline.TimelineViewModel
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@AndroidEntryPoint @AndroidEntryPoint
class DMTimelineDialogFragment : class DMTimelineDialogFragment :
BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate) { BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate) {
private val viewModel by viewModels<DMTimelineViewModel>() private val viewModel by viewModels<TimelineViewModel>()
private val navigator by lazy { DMTimelineNavigator(this) } private val navigator by lazy { DMTimelineNavigator(this) }
private val videoPlayer by lazy { private val videoPlayer by lazy {
...@@ -64,10 +65,10 @@ class DMTimelineDialogFragment : ...@@ -64,10 +65,10 @@ class DMTimelineDialogFragment :
private fun setupObservers() { private fun setupObservers() {
viewModel.dmRoomLiveData.observeData(this) { summaryOptional -> // viewModel.dmRoomLiveData.observeData(this) { summaryOptional ->
val summary = summaryOptional.getOrNull() ?: return@observeData // val summary = summaryOptional.getOrNull() ?: return@observeData
setupToolBar(summary) // setupToolBar(summary)
} // }
} }
private fun setupToolBar(dmRoomSummary: RoomSummary) { private fun setupToolBar(dmRoomSummary: RoomSummary) {
......
package org.futo.circles.core.feature.direct package org.futo.circles.feature.direct.timeline
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import org.futo.circles.core.extensions.navigateSafe import org.futo.circles.core.extensions.navigateSafe
......
package org.futo.circles.core.feature.direct.list package org.futo.circles.feature.direct.timeline.list
class DMTimelineAdapter { class DMTimelineAdapter {
} }
\ No newline at end of file
package org.futo.circles.core.feature.direct.list package org.futo.circles.feature.direct.timeline.list
class DMTimelineViewHolder { class DMTimelineViewHolder {
} }
\ No newline at end of file
...@@ -7,9 +7,13 @@ ...@@ -7,9 +7,13 @@
<dialog <dialog
android:id="@+id/DMTimelineDialogFragment" android:id="@+id/DMTimelineDialogFragment"
android:name="org.futo.circles.core.feature.direct.DMTimelineDialogFragment" android:name="org.futo.circles.feature.direct.timeline.DMTimelineDialogFragment"
tools:layout="@layout/dialog_fragment_dm_timeline"> tools:layout="@layout/dialog_fragment_dm_timeline">
<deepLink
android:id="@+id/dmTimelineDeepLink"
app:uri="circles://app/dmTimeline/{roomId}" />
<argument <argument
android:name="roomId" android:name="roomId"
app:argType="string" app:argType="string"
......
package org.futo.circles.core.extensions package org.futo.circles.core.extensions
import android.net.Uri
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
...@@ -7,3 +8,4 @@ import org.matrix.android.sdk.api.extensions.tryOrNull ...@@ -7,3 +8,4 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
fun NavController.navigateSafe(directions: NavDirections) = tryOrNull { navigate(directions) } fun NavController.navigateSafe(directions: NavDirections) = tryOrNull { navigate(directions) }
fun NavController.navigateSafe(@IdRes resId: Int) = tryOrNull { navigate(resId) } fun NavController.navigateSafe(@IdRes resId: Int) = tryOrNull { navigate(resId) }
fun NavController.navigateSafe(deepLink: Uri) = tryOrNull { navigate(deepLink) }
package org.futo.circles.core.feature.direct
import androidx.lifecycle.SavedStateHandle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.update
import org.futo.circles.core.extensions.getOrThrow
import org.futo.circles.core.feature.timeline.builder.SingleTimelineBuilder
import org.futo.circles.core.model.Post
import org.futo.circles.core.model.PostListItem
import org.futo.circles.core.model.TimelineLoadingItem
import org.futo.circles.core.provider.MatrixSessionProvider
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
class DMTimelineDataSource(
savedStateHandle: SavedStateHandle,
private val timelineBuilder: SingleTimelineBuilder
) {
private val roomId: String = savedStateHandle.getOrThrow("roomId")
private val session = MatrixSessionProvider.getSessionOrThrow()
val room = session.getRoom(roomId) ?: throw IllegalArgumentException("room is not found")
private val listDirection = Timeline.Direction.FORWARDS
private val isThread = false
private val pageLoadingFlow = MutableStateFlow(false)
fun getTimelineEventFlow(viewModelScope: CoroutineScope): Flow<List<PostListItem>> = combine(
pageLoadingFlow,
getPostEventsFlow(viewModelScope)
) { isLoading, events ->
if (isLoading) {
mutableListOf<PostListItem>().apply {
addAll(events)
add(TimelineLoadingItem())
}
} else events
}.flowOn(Dispatchers.IO).distinctUntilChanged()
private fun getPostEventsFlow(viewModelScope: CoroutineScope): Flow<List<Post>> = callbackFlow {
val listener = object : Timeline.Listener {
override fun onTimelineUpdated(
roomId: String,
timelineId: String,
snapshot: List<TimelineEvent>
) {
if (snapshot.isNotEmpty()) trySend(roomId to snapshot)
}
override fun onTimelineFailure(timelineId: String, throwable: Throwable) {
onRestartTimeline(timelineId, throwable)
}
override fun onNewTimelineEvents(eventIds: List<String>) {
super.onNewTimelineEvents(eventIds)
pageLoadingFlow.update { false }
}
}
startTimeline(viewModelScope, listener)
awaitClose()
}.flowOn(Dispatchers.IO)
.mapLatest { (roomId, snapshot) -> timelineBuilder.build(roomId, snapshot, isThread) }
.distinctUntilChanged()
protected abstract fun startTimeline(
viewModelScope: CoroutineScope,
listener: Timeline.Listener
)
protected abstract fun onRestartTimeline(timelineId: String, throwable: Throwable)
abstract fun clearTimeline()
abstract suspend fun loadMore(showLoader: Boolean)
protected fun createAndStartNewTimeline(room: Room, listener: Timeline.Listener) =
room.timelineService()
.createTimeline(
null,
TimelineSettings(initialSize = MESSAGES_PER_PAGE, rootThreadEventId = threadEventId)
)
.apply {
addListener(listener)
start(threadEventId)
}
private fun closeTimeline(timeline: Timeline) {
timeline.removeAllListeners()
timeline.dispose()
}
private suspend fun loadNextPage(showLoader: Boolean, timeline: Timeline) {
if (timeline.hasMoreToLoad(listDirection)) {
pageLoadingFlow.update { showLoader }
var snapshot = timeline.awaitPaginate(listDirection, MESSAGES_PER_PAGE)
var postsLoadedCount = timelineBuilder.filterTimelineEvents(snapshot, isThread).size
while (postsLoadedCount < MIN_MESSAGES_ON_PAGE && timeline.hasMoreToLoad(listDirection)) {
snapshot = timeline.awaitPaginate(listDirection, MESSAGES_PER_PAGE)
postsLoadedCount = timelineBuilder.filterTimelineEvents(snapshot, isThread).size
}
timeline.postCurrentSnapshot()
} else {
pageLoadingFlow.update { false }
}
}
companion object {
private const val MESSAGES_PER_PAGE = 20
private const val MIN_MESSAGES_ON_PAGE = 10
}
}
\ No newline at end of file
package org.futo.circles.core.feature.direct
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import org.futo.circles.core.extensions.getOrThrow
import org.futo.circles.core.provider.MatrixSessionProvider
import org.matrix.android.sdk.api.session.getRoom
import javax.inject.Inject
@HiltViewModel
class DMTimelineViewModel @Inject constructor(
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val roomId: String = savedStateHandle.getOrThrow("roomId")
private val session = MatrixSessionProvider.getSessionOrThrow()
val dmRoomLiveData = session.getRoom(roomId)?.getRoomSummaryLive()
?: throw IllegalArgumentException("dm room $roomId is not found")
fun loadMore() {
}
}
\ No newline at end of file
package org.futo.circles.core.feature.user package org.futo.circles.core.feature.user
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
...@@ -184,7 +185,7 @@ class UserDialogFragment : ...@@ -184,7 +185,7 @@ class UserDialogFragment :
setText(getString(R.string.open_direct_messages)) setText(getString(R.string.open_direct_messages))
setOnClickListener { setOnClickListener {
findNavController().navigateSafe( findNavController().navigateSafe(
UserDialogFragmentDirections.toDmTimelineNavGraph(roomId) Uri.parse("circles://app/dmTimeline/$roomId")
) )
} }
} }
......
...@@ -88,7 +88,9 @@ fun getUserDirectMessagesRoomLiveData( ...@@ -88,7 +88,9 @@ fun getUserDirectMessagesRoomLiveData(
.getRoomSummariesLive(roomSummaryQueryParams { .getRoomSummariesLive(roomSummaryQueryParams {
memberships = membershipFilter memberships = membershipFilter
roomCategoryFilter = RoomCategoryFilter.ONLY_DM roomCategoryFilter = RoomCategoryFilter.ONLY_DM
}).map { summaries -> summaries.firstOrNull { it.directUserId == userId } } }).map { summaries ->
summaries.firstOrNull { it.directUserId == userId }
}
fun getUserDirectMessagesStateLiveData( fun getUserDirectMessagesStateLiveData(
......
...@@ -35,21 +35,8 @@ ...@@ -35,21 +35,8 @@
app:nullable="false" /> app:nullable="false" />
</action> </action>
<!-- DM nav graph includes user graph,
navigate by only destination id here to avoid loops-->
<action
android:id="@+id/to_dm_timeline_nav_graph"
app:destination="@id/dm_timeline_nav_graph">
<argument
android:name="roomId"
app:argType="string"
app:nullable="false" />
</action>
</dialog> </dialog>
<dialog <dialog
android:id="@+id/inviteToFollowMeDialogFragment" android:id="@+id/inviteToFollowMeDialogFragment"
android:name="org.futo.circles.core.feature.invite_to_follow.InviteToFollowMeDialogFragment" android:name="org.futo.circles.core.feature.invite_to_follow.InviteToFollowMeDialogFragment"
......