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 (5)
...@@ -5,21 +5,35 @@ import android.view.View ...@@ -5,21 +5,35 @@ import android.view.View
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.futo.circles.R
import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment
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.extensions.observeResponse
import org.futo.circles.feature.timeline.TimelineViewModel import org.futo.circles.core.extensions.showNoInternetConnection
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.futo.circles.core.extensions.showSuccess
import org.futo.circles.core.extensions.withConfirmation
import org.futo.circles.core.feature.share.ShareProvider
import org.futo.circles.core.model.PostContent
import org.futo.circles.databinding.DialogFragmentDmTimelineBinding
import org.futo.circles.feature.timeline.list.PostOptionsListener
import org.futo.circles.feature.timeline.list.TimelineAdapter
import org.futo.circles.feature.timeline.post.create.PostSentListener
import org.futo.circles.feature.timeline.post.emoji.EmojiPickerListener
import org.futo.circles.feature.timeline.post.menu.PostMenuListener
import org.futo.circles.model.RemovePost
@AndroidEntryPoint @AndroidEntryPoint
class DMTimelineDialogFragment : class DMTimelineDialogFragment :
BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate) { BaseFullscreenDialogFragment<DialogFragmentDmTimelineBinding>(DialogFragmentDmTimelineBinding::inflate),
PostOptionsListener, PostMenuListener, EmojiPickerListener, PostSentListener {
private val viewModel by viewModels<TimelineViewModel>() private val args: DMTimelineDialogFragmentArgs by navArgs()
private val navigator by lazy { DMTimelineNavigator(this) } private val viewModel by viewModels<DMTimelineViewModel>()
private val videoPlayer by lazy { private val videoPlayer by lazy {
ExoPlayer.Builder(requireContext()).build().apply { ExoPlayer.Builder(requireContext()).build().apply {
...@@ -27,21 +41,25 @@ class DMTimelineDialogFragment : ...@@ -27,21 +41,25 @@ class DMTimelineDialogFragment :
} }
} }
// private val listAdapter by lazy { private val navigator by lazy { DMTimelineNavigator(this) }
// DMTimelineAdapter(this, isThread, videoPlayer).apply { private val listAdapter by lazy {
// setHasStableIds(true) TimelineAdapter(this, false, videoPlayer).apply {
// } setHasStableIds(true)
// } }
}
private var onLocalAddEmojiCallback: ((String) -> Unit)? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupViews() setupViews()
setupObservers() setupObservers()
stopVideoOnNewScreenOpen()
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
//listAdapter.stopVideoPlayback() listAdapter.stopVideoPlayback()
} }
override fun onDestroy() { override fun onDestroy() {
...@@ -53,32 +71,113 @@ class DMTimelineDialogFragment : ...@@ -53,32 +71,113 @@ class DMTimelineDialogFragment :
private fun setupViews() { private fun setupViews() {
binding.rvTimeline.apply { binding.rvTimeline.apply {
//adapter = listAdapter adapter = listAdapter
getRecyclerView().apply { getRecyclerView().apply {
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
clipToPadding = false clipToPadding = false
setPadding(paddingLeft, paddingTop, paddingRight, context.dpToPx(70)) setPadding(paddingLeft, paddingTop, paddingRight, context.dpToPx(70))
} }
//addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
addPageEndListener { viewModel.loadMore() } addPageEndListener { viewModel.loadMore() }
} }
} }
private fun setupObservers() { private fun setupObservers() {
// viewModel.dmRoomLiveData.observeData(this) { summaryOptional -> viewModel.titleLiveData.observeData(this) { roomName ->
// val summary = summaryOptional.getOrNull() ?: return@observeData binding.toolbar.apply {
// setupToolBar(summary) title = roomName
// } setOnClickListener { }
} }
}
private fun setupToolBar(dmRoomSummary: RoomSummary) { viewModel.timelineEventsLiveData.observeData(this) {
val userId = dmRoomSummary.directUserId ?: "" listAdapter.submitList(it)
with(binding.toolbar) { viewModel.markTimelineAsRead(args.roomId, true)
title = dmRoomSummary.nameOrId() }
subtitle = userId viewModel.shareLiveData.observeData(this) { content ->
setOnClickListener { navigator.navigateToUserPage(userId) } context?.let { ShareProvider.share(it, content) }
} }
viewModel.saveToDeviceLiveData.observeData(this) {
context?.let { showSuccess(it.getString(R.string.saved)) }
}
viewModel.unSendReactionLiveData.observeResponse(this)
} }
override fun onPostSent() {
scrollToTopOnMyNewPostAdded()
}
override fun onShowMenuClicked(roomId: String, eventId: String) {
if (showNoInternetConnection()) return
//navigator.navigatePostMenu(roomId, eventId)
}
override fun onUserClicked(userId: String) {
//navigator.navigateToUserDialogFragment(userId)
}
override fun onShowPreview(roomId: String, eventId: String) {
//navigator.navigateToShowMediaPreview(roomId, eventId)
}
override fun onShowEmoji(roomId: String, eventId: String, onAddEmoji: (String) -> Unit) {
if (showNoInternetConnection()) return
//if (showErrorIfNotAbleToPost()) return
onLocalAddEmojiCallback = onAddEmoji
//navigator.navigateToShowEmoji(roomId, eventId)
}
override fun onReply(roomId: String, eventId: String) {
}
override fun onShare(content: PostContent, view: View) {
viewModel.sharePostContent(content, view)
}
override fun onRemove(roomId: String, eventId: String) {
withConfirmation(RemovePost()) { viewModel.removeMessage(roomId, eventId) }
}
override fun onIgnore(senderId: String) {
}
override fun onSaveToDevice(content: PostContent) {
viewModel.saveToDevice(content)
}
override fun onEmojiChipClicked(
roomId: String, eventId: String, emoji: String, isUnSend: Boolean
) {
if (showNoInternetConnection()) return
//if (showErrorIfNotAbleToPost()) return
if (isUnSend) viewModel.unSendReaction(roomId, eventId, emoji)
else viewModel.sendReaction(roomId, eventId, emoji)
}
override fun onPollOptionSelected(roomId: String, eventId: String, optionId: String) {
}
override fun endPoll(roomId: String, eventId: String) {
}
override fun onEmojiSelected(roomId: String?, eventId: String?, emoji: String) {
roomId ?: return
eventId ?: return
onLocalAddEmojiCallback?.invoke(emoji)
onLocalAddEmojiCallback = null
viewModel.sendReaction(roomId, eventId, emoji)
}
private fun stopVideoOnNewScreenOpen() {
findNavController().addOnDestinationChangedListener { _, destination, _ ->
if (destination.id != R.id.timelineFragment) listAdapter.stopVideoPlayback()
}
}
private fun scrollToTopOnMyNewPostAdded() {
binding.rvTimeline.adapter?.itemCount?.let { count ->
binding.rvTimeline.layoutManager?.scrollToPosition(count - 1)
}
}
} }
\ No newline at end of file
package org.futo.circles.feature.direct.timeline
import android.content.Context
import android.view.View
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import org.futo.circles.core.base.SingleEventLiveData
import org.futo.circles.core.extensions.Response
import org.futo.circles.core.extensions.launchBg
import org.futo.circles.core.feature.circles.filter.CircleFilterAccountDataManager
import org.futo.circles.core.feature.timeline.BaseTimelineViewModel
import org.futo.circles.core.feature.timeline.data_source.BaseTimelineDataSource
import org.futo.circles.core.feature.timeline.post.PostOptionsDataSource
import org.futo.circles.core.model.PostContent
import org.futo.circles.core.model.ShareableContent
import org.futo.circles.core.provider.MatrixSessionProvider
import org.futo.circles.feature.timeline.data_source.ReadMessageDataSource
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.util.Cancelable
import javax.inject.Inject
@HiltViewModel
class DMTimelineViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
@ApplicationContext context: Context,
timelineDataSourceFactory: BaseTimelineDataSource.Factory,
private val postOptionsDataSource: PostOptionsDataSource,
private val readMessageDataSource: ReadMessageDataSource,
circleFilterAccountDataManager: CircleFilterAccountDataManager
) : BaseTimelineViewModel(
savedStateHandle,
context,
timelineDataSourceFactory.create(false),
circleFilterAccountDataManager
) {
val session = MatrixSessionProvider.currentSession
val shareLiveData = SingleEventLiveData<ShareableContent>()
val saveToDeviceLiveData = SingleEventLiveData<Unit>()
val unSendReactionLiveData = SingleEventLiveData<Response<Cancelable?>>()
fun sharePostContent(content: PostContent, view: View) {
launchBg {
postOptionsDataSource.getShareableContent(content, view)
?.let { shareLiveData.postValue(it) }
}
}
fun removeMessage(roomId: String, eventId: String) {
launchBg { postOptionsDataSource.removeMessage(roomId, eventId) }
}
fun saveToDevice(content: PostContent) {
launchBg {
postOptionsDataSource.saveMediaToDevice(content)
saveToDeviceLiveData.postValue(Unit)
}
}
fun sendReaction(roomId: String, eventId: String, emoji: String) {
postOptionsDataSource.sendReaction(roomId, eventId, emoji)
}
fun unSendReaction(roomId: String, eventId: String, emoji: String) {
launchBg {
val result = postOptionsDataSource.unSendReaction(roomId, eventId, emoji)
unSendReactionLiveData.postValue(result)
}
}
fun markTimelineAsRead(roomId: String, isGroup: Boolean) {
launchBg {
if (isGroup) readMessageDataSource.markRoomAsRead(roomId)
else session?.getRoom(roomId)?.roomSummary()?.spaceChildren?.map {
async { readMessageDataSource.markRoomAsRead(it.childRoomId) }
}?.awaitAll()
}
}
}
\ No newline at end of file
package org.futo.circles.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import org.futo.circles.core.model.Post
import org.futo.circles.databinding.ViewPostFooterBinding
import org.futo.circles.databinding.ViewSendMessageBinding
import org.futo.circles.feature.timeline.list.PostOptionsListener
class SendMessageView(
context: Context,
attrs: AttributeSet? = null,
) : ConstraintLayout(context, attrs) {
private val binding =
ViewSendMessageBinding.inflate(LayoutInflater.from(context), this)
}
\ No newline at end of file
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
app:layout_constraintTop_toBottomOf="@id/toolbarDivider" /> app:layout_constraintTop_toBottomOf="@id/toolbarDivider" />
<EditText <org.futo.circles.view.SendMessageView
android:id="@+id/lCreatePost" android:id="@+id/lCreatePost"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
...@@ -45,4 +45,5 @@ ...@@ -45,4 +45,5 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/lCreatePost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnEmoji"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:background="?selectableItemBackgroundBorderless"
android:enabled="false"
android:focusable="true"
android:minWidth="0dp"
android:minHeight="0dp"
android:padding="0dp"
app:icon="@drawable/ic_emoji"
app:iconGravity="end"
app:iconSize="40dp"
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/etMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@null"
android:hint="@string/message"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnAddImage"
app:layout_constraintStart_toEndOf="@id/btnEmoji"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAddImage"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?selectableItemBackgroundBorderless"
android:enabled="false"
android:focusable="true"
android:minWidth="0dp"
android:minHeight="0dp"
android:padding="0dp"
app:icon="@drawable/ic_image"
app:iconGravity="end"
app:iconSize="40dp"
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnSend"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnSend"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?selectableItemBackgroundBorderless"
android:enabled="false"
android:focusable="true"
android:minWidth="0dp"
android:minHeight="0dp"
android:padding="0dp"
app:icon="@drawable/ic_send"
app:iconGravity="end"
app:iconSize="40dp"
app:iconTint="@color/send_ic_state_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</merge>