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 @@
</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
android:name=".feature.share.circle.ShareWithCircleActivity"
......
......@@ -3,10 +3,10 @@ package org.futo.circles.feature.direct.create.list
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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.onClick
import org.futo.circles.core.model.CirclesUserSummary
import org.futo.circles.databinding.ListItemCreateDmBinding
class CreateDMUserViewHolder(
parent: ViewGroup,
......
package org.futo.circles.core.feature.direct
package org.futo.circles.feature.direct.timeline
import android.os.Bundle
import android.view.View
......@@ -11,13 +11,14 @@ import org.futo.circles.core.databinding.DialogFragmentDmTimelineBinding
import org.futo.circles.core.extensions.dpToPx
import org.futo.circles.core.extensions.observeData
import org.futo.circles.core.mapping.nameOrId
import org.futo.circles.feature.timeline.TimelineViewModel
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@AndroidEntryPoint
class DMTimelineDialogFragment :
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 videoPlayer by lazy {
......@@ -64,10 +65,10 @@ class DMTimelineDialogFragment :
private fun setupObservers() {
viewModel.dmRoomLiveData.observeData(this) { summaryOptional ->
val summary = summaryOptional.getOrNull() ?: return@observeData
setupToolBar(summary)
}
// viewModel.dmRoomLiveData.observeData(this) { summaryOptional ->
// val summary = summaryOptional.getOrNull() ?: return@observeData
// setupToolBar(summary)
// }
}
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 org.futo.circles.core.extensions.navigateSafe
......
package org.futo.circles.core.feature.direct.list
package org.futo.circles.feature.direct.timeline.list
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 {
}
\ No newline at end of file
......@@ -7,9 +7,13 @@
<dialog
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">
<deepLink
android:id="@+id/dmTimelineDeepLink"
app:uri="circles://app/dmTimeline/{roomId}" />
<argument
android:name="roomId"
app:argType="string"
......
package org.futo.circles.core.extensions
import android.net.Uri
import androidx.annotation.IdRes
import androidx.navigation.NavController
import androidx.navigation.NavDirections
......@@ -7,3 +8,4 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
fun NavController.navigateSafe(directions: NavDirections) = tryOrNull { navigate(directions) }
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
import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
......@@ -184,7 +185,7 @@ class UserDialogFragment :
setText(getString(R.string.open_direct_messages))
setOnClickListener {
findNavController().navigateSafe(
UserDialogFragmentDirections.toDmTimelineNavGraph(roomId)
Uri.parse("circles://app/dmTimeline/$roomId")
)
}
}
......
......@@ -88,7 +88,9 @@ fun getUserDirectMessagesRoomLiveData(
.getRoomSummariesLive(roomSummaryQueryParams {
memberships = membershipFilter
roomCategoryFilter = RoomCategoryFilter.ONLY_DM
}).map { summaries -> summaries.firstOrNull { it.directUserId == userId } }
}).map { summaries ->
summaries.firstOrNull { it.directUserId == userId }
}
fun getUserDirectMessagesStateLiveData(
......
......@@ -35,21 +35,8 @@
app:nullable="false" />
</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
android:id="@+id/inviteToFollowMeDialogFragment"
android:name="org.futo.circles.core.feature.invite_to_follow.InviteToFollowMeDialogFragment"
......