Threads init commit
This commit is contained in:
parent
aea22201c3
commit
ab87937e5b
@ -28,6 +28,9 @@ object RelationType {
|
||||
/** Lets you define an event which references an existing event.*/
|
||||
const val REFERENCE = "m.reference"
|
||||
|
||||
/** Lets you define an event which is a reply to an existing event.*/
|
||||
const val THREAD = "m.thread"
|
||||
|
||||
/** Lets you define an event which adds a response to an existing event.*/
|
||||
const val RESPONSE = "org.matrix.response"
|
||||
}
|
||||
|
@ -44,6 +44,9 @@ import org.matrix.android.sdk.api.util.Optional
|
||||
* m.reference - lets you define an event which references an existing event.
|
||||
* When aggregated, currently doesn't do anything special, but in future could bundle chains of references (i.e. threads).
|
||||
* These are primarily intended for handling replies (and in future threads).
|
||||
*
|
||||
* m.thread - lets you define an event which is a thread reply to an existing event.
|
||||
* When aggregated, returns the most thread event
|
||||
*/
|
||||
interface RelationService {
|
||||
|
||||
@ -123,4 +126,16 @@ interface RelationService {
|
||||
* @return the LiveData of EventAnnotationsSummary
|
||||
*/
|
||||
fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>>
|
||||
|
||||
/**
|
||||
* Creates a thread reply for an existing timeline event
|
||||
* The replyInThreadText can be a Spannable and contains special spans (MatrixItemSpan) that will be translated
|
||||
* by the sdk into pills.
|
||||
* @param eventToReplyInThread the event referenced by the thread reply
|
||||
* @param replyInThreadText the reply text
|
||||
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
|
||||
*/
|
||||
fun replyInThread(eventToReplyInThread: TimelineEvent,
|
||||
replyInThreadText: CharSequence,
|
||||
autoMarkdown: Boolean = false): Cancelable?
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.model.relation.threads
|
||||
|
||||
interface ThreadContent {
|
||||
|
||||
companion object {
|
||||
const val MSG_TYPE_JSON_KEY = "msgtype"
|
||||
}
|
||||
|
||||
val msgType: String
|
||||
val body: String
|
||||
val relatesTo: ThreadRelatesTo?
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.model.relation.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ThreadRelatesTo(
|
||||
@Json(name = "rel_type") override val type: String? = RelationType.THREAD,
|
||||
@Json(name = "event_id") override val eventId: String,
|
||||
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null,
|
||||
@Json(name = "option") override val option: Int? = null
|
||||
) : RelationContent
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.room.model.relation.threads
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ThreadTextContent(
|
||||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String,
|
||||
@Json(name = "body") override val body: String,
|
||||
@Json(name = "m.relates_to") override val relatesTo: ThreadRelatesTo? = null,
|
||||
) : ThreadContent
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.TextContent
|
||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
@ -158,6 +159,11 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun replyInThread(eventToReplyInThread: TimelineEvent, replyInThreadText: CharSequence, autoMarkdown: Boolean): Cancelable? {
|
||||
val event = eventFactory.createThreadTextEvent(eventToReplyInThread, TextContent(replyInThreadText.toString()))
|
||||
return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the event in database as a local echo.
|
||||
* SendState is set to UNSENT and it's added to a the sendingTimelineEvents list of the room.
|
||||
|
@ -51,6 +51,8 @@ import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.threads.ThreadTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.threads.ThreadRelatesTo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.isReply
|
||||
@ -58,6 +60,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor
|
||||
import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory
|
||||
import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@ -340,6 +343,15 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thread event related to the already existing event
|
||||
*/
|
||||
fun createThreadTextEvent(eventToReplyInThread: TimelineEvent, textContent: TextContent): Event =
|
||||
createEvent(
|
||||
eventToReplyInThread.roomId,
|
||||
EventType.MESSAGE,
|
||||
textContent.toThreadTextContent(eventToReplyInThread).toContent())
|
||||
|
||||
private fun dummyOriginServerTs(): Long {
|
||||
return System.currentTimeMillis()
|
||||
}
|
||||
|
@ -16,9 +16,13 @@
|
||||
|
||||
package org.matrix.android.sdk.internal.session.room.send
|
||||
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.threads.ThreadTextContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.threads.ThreadRelatesTo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.ContentUtils.extractUsefulTextFromHtmlReply
|
||||
import org.matrix.android.sdk.api.util.ContentUtils.extractUsefulTextFromReply
|
||||
|
||||
@ -41,6 +45,14 @@ fun TextContent.toMessageTextContent(msgType: String = MessageType.MSGTYPE_TEXT)
|
||||
)
|
||||
}
|
||||
|
||||
fun TextContent.toThreadTextContent(eventToReplyInThread: TimelineEvent, msgType: String = MessageType.MSGTYPE_TEXT): ThreadTextContent {
|
||||
return ThreadTextContent(
|
||||
msgType = msgType,
|
||||
body = text,
|
||||
relatesTo = ThreadRelatesTo(eventId = eventToReplyInThread.eventId)
|
||||
)
|
||||
}
|
||||
|
||||
fun TextContent.removeInReplyFallbacks(): TextContent {
|
||||
return copy(
|
||||
text = extractUsefulTextFromReply(this.text),
|
||||
|
@ -179,6 +179,9 @@
|
||||
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
||||
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
||||
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
||||
<activity android:name=".features.home.room.threads.RoomThreadsActivity" />
|
||||
<activity android:name=".features.home.room.threads.detail.RoomThreadDetailActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".features.home.room.detail.RoomDetailActivity"
|
||||
android:parentActivityName=".features.home.HomeActivity">
|
||||
|
@ -58,6 +58,7 @@ import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
|
||||
import im.vector.app.features.home.room.detail.RoomDetailFragment
|
||||
import im.vector.app.features.home.room.detail.search.SearchFragment
|
||||
import im.vector.app.features.home.room.list.RoomListFragment
|
||||
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailFragment
|
||||
import im.vector.app.features.login.LoginCaptchaFragment
|
||||
import im.vector.app.features.login.LoginFragment
|
||||
import im.vector.app.features.login.LoginGenericTextInputFormFragment
|
||||
@ -834,4 +835,9 @@ interface FragmentModule {
|
||||
@IntoMap
|
||||
@FragmentKey(SpaceLeaveAdvancedFragment::class)
|
||||
fun bindSpaceLeaveAdvancedFragment(fragment: SpaceLeaveAdvancedFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(RoomThreadDetailFragment::class)
|
||||
fun bindRoomThreadDetailFragment(fragment: RoomThreadDetailFragment): Fragment
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
|
||||
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
|
||||
import im.vector.app.features.home.room.list.RoomListModule
|
||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||
import im.vector.app.features.home.room.threads.RoomThreadsActivity
|
||||
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
|
||||
import im.vector.app.features.invite.AutoAcceptInvites
|
||||
import im.vector.app.features.invite.InviteUsersToRoomActivity
|
||||
import im.vector.app.features.invite.VectorInviteView
|
||||
@ -174,6 +176,8 @@ interface ScreenComponent {
|
||||
fun inject(activity: SpaceManageActivity)
|
||||
fun inject(activity: RoomJoinRuleActivity)
|
||||
fun inject(activity: SpaceLeaveAdvancedActivity)
|
||||
fun inject(activity: RoomThreadsActivity)
|
||||
fun inject(activity: RoomThreadDetailActivity)
|
||||
|
||||
/* ==========================================================================================
|
||||
* BottomSheets
|
||||
|
@ -161,6 +161,8 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
|
||||
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews
|
||||
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
|
||||
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailArgs
|
||||
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
|
||||
import im.vector.app.features.html.EventHtmlRenderer
|
||||
import im.vector.app.features.html.PillImageSpan
|
||||
import im.vector.app.features.html.PillsPostProcessor
|
||||
@ -1957,6 +1959,16 @@ class RoomDetailFragment @Inject constructor(
|
||||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||
}
|
||||
}
|
||||
is EventSharedAction.ReplyInThread -> {
|
||||
if (!views.voiceMessageRecorderView.isActive()) {
|
||||
context?.let {
|
||||
val roomThreadDetailArgs = RoomThreadDetailArgs(roomDetailArgs.roomId,action.eventId)
|
||||
startActivity(RoomThreadDetailActivity.newIntent(it, roomThreadDetailArgs))
|
||||
}
|
||||
} else {
|
||||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||
}
|
||||
}
|
||||
is EventSharedAction.CopyPermalink -> {
|
||||
val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
|
||||
copyToClipboard(requireContext(), permalink, false)
|
||||
|
@ -48,6 +48,10 @@ sealed class EventSharedAction(@StringRes val titleRes: Int,
|
||||
data class Reply(val eventId: String) :
|
||||
EventSharedAction(R.string.reply, R.drawable.ic_reply)
|
||||
|
||||
data class ReplyInThread(val eventId: String) :
|
||||
// TODO add translations
|
||||
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
||||
|
||||
data class Share(val eventId: String, val messageContent: MessageContent) :
|
||||
EventSharedAction(R.string.share, R.drawable.ic_share)
|
||||
|
||||
|
@ -326,6 +326,11 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||
add(EventSharedAction.Reply(eventId))
|
||||
}
|
||||
|
||||
// *** Testing Threads ****
|
||||
if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) {
|
||||
add(EventSharedAction.ReplyInThread(eventId))
|
||||
}
|
||||
|
||||
if (canEdit(timelineEvent, session.myUserId, actionPermissions)) {
|
||||
add(EventSharedAction.Edit(eventId))
|
||||
}
|
||||
@ -412,6 +417,22 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||
}
|
||||
}
|
||||
|
||||
private fun canReplyInThread(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
if (!actionPermissions.canSendMessage) return false
|
||||
return when (messageContent?.msgType) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.threads
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.extensions.replaceFragment
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityFilteredRoomsBinding
|
||||
import im.vector.app.databinding.ActivityRoomThreadsBinding
|
||||
import im.vector.app.features.home.RoomListDisplayMode
|
||||
import im.vector.app.features.home.room.list.RoomListFragment
|
||||
import im.vector.app.features.home.room.list.RoomListParams
|
||||
|
||||
class RoomThreadsActivity : VectorBaseActivity<ActivityRoomThreadsBinding>() {
|
||||
|
||||
// private val roomListFragment: RoomListFragment?
|
||||
// get() {
|
||||
// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
|
||||
// }
|
||||
|
||||
override fun getBinding() = ActivityRoomThreadsBinding.inflate(layoutInflater)
|
||||
|
||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun getMenuRes() = R.menu.menu_room_threads
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
configureToolbar(views.roomThreadsToolbar)
|
||||
// if (isFirstCreation()) {
|
||||
// val params = RoomListParams(RoomListDisplayMode.FILTERED)
|
||||
// replaceFragment(R.id.filteredRoomsFragmentContainer, RoomListFragment::class.java, params, FRAGMENT_TAG)
|
||||
// }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FRAGMENT_TAG = "RoomListFragment"
|
||||
|
||||
fun newIntent(context: Context): Intent {
|
||||
return Intent(context, RoomThreadsActivity::class.java)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.threads.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.extensions.replaceFragment
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityRoomThreadDetailBinding
|
||||
|
||||
class RoomThreadDetailActivity : VectorBaseActivity<ActivityRoomThreadDetailBinding>() {
|
||||
|
||||
// private val roomThreadDetailFragment: RoomThreadDetailFragment?
|
||||
// get() {
|
||||
// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomThreadDetailFragment
|
||||
// }
|
||||
|
||||
override fun getBinding() = ActivityRoomThreadDetailBinding.inflate(layoutInflater)
|
||||
|
||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun getMenuRes() = R.menu.menu_room_threads
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
configureToolbar(views.roomThreadDetailToolbar)
|
||||
if (isFirstCreation()) {
|
||||
val roomThreadDetailArgs: RoomThreadDetailArgs? = intent?.extras?.getParcelable(EXTRA_ROOM_THREAD_DETAIL_ARGS)
|
||||
replaceFragment(R.id.roomThreadDetailFragmentContainer, RoomThreadDetailFragment::class.java, roomThreadDetailArgs, FRAGMENT_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FRAGMENT_TAG = "RoomThreadDetailFragment"
|
||||
const val EXTRA_ROOM_THREAD_DETAIL_ARGS = "EXTRA_ROOM_THREAD_DETAIL_ARGS"
|
||||
|
||||
fun newIntent(context: Context, roomThreadDetailArgs: RoomThreadDetailArgs): Intent {
|
||||
return Intent(context, RoomThreadDetailActivity::class.java).apply {
|
||||
putExtra(EXTRA_ROOM_THREAD_DETAIL_ARGS, roomThreadDetailArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.threads.detail
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.args
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentRoomThreadDetailBinding
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class RoomThreadDetailArgs(
|
||||
val roomId: String,
|
||||
val eventId: String? = null,
|
||||
) : Parcelable
|
||||
|
||||
class RoomThreadDetailFragment @Inject constructor(
|
||||
private val session: Session
|
||||
) :
|
||||
VectorBaseFragment<FragmentRoomThreadDetailBinding>() {
|
||||
private val roomThreadDetailArgs: RoomThreadDetailArgs by args()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomThreadDetailBinding {
|
||||
return FragmentRoomThreadDetailBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
views.testTextVeiwddasda.text = "${roomThreadDetailArgs.eventId} -- ${roomThreadDetailArgs.roomId}"
|
||||
}
|
||||
}
|
24
vector/src/main/res/drawable/ic_reply_in_thread.xml
Normal file
24
vector/src/main/res/drawable/ic_reply_in_thread.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="20dp"
|
||||
android:height="21dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="21"
|
||||
>
|
||||
|
||||
<group>
|
||||
|
||||
<clip-path
|
||||
android:pathData="M3 1H17C18.1046 1 19 1.89543 19 3V18C19 19.1046 18.1046 20 17 20H3C1.89543 20 1 19.1046 1 18V3C1 1.89543 1.89543 1 3 1Z"
|
||||
/>
|
||||
|
||||
</group>
|
||||
|
||||
<path
|
||||
android:pathData="M3 1H17C18.1046 1 19 1.89543 19 3V18C19 19.1046 18.1046 20 17 20H3C1.89543 20 1 19.1046 1 18V3C1 1.89543 1.89543 1 3 1Z"
|
||||
android:strokeWidth="2"
|
||||
android:strokeColor="#737D8C"
|
||||
/>
|
||||
|
||||
</vector>
|
88
vector/src/main/res/layout/activity_room_thread_detail.xml
Normal file
88
vector/src/main/res/layout/activity_room_thread_detail.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/roomThreadDetailToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:contentInsetStart="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/roomThreadDetailToolbarConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomThreadDetailToolbarTitleTextView"
|
||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/a11y_beta"
|
||||
android:textColor="?vctr_content_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/roomThreadDetailToolbarImageView"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_presence_offline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarTitleTextView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/room_round_avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomThreadDetailToolbarSubtitleTextView"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="RoomName"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/roomThreadDetailFragmentContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
88
vector/src/main/res/layout/activity_room_threads.xml
Normal file
88
vector/src/main/res/layout/activity_room_threads.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/roomThreadsToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:contentInsetStart="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/roomThreadsToolbarConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomThreadsToolbarTitleTextView"
|
||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/a11y_beta"
|
||||
android:textColor="?vctr_content_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/roomThreadsToolbarImageView"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_presence_offline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarTitleTextView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/room_round_avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomThreadsToolbarSubtitleTextView"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?vctr_content_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="RoomName"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/roomThreadsFragmentContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -18,7 +18,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/roomToolbar"
|
||||
|
33
vector/src/main/res/layout/fragment_room_thread_detail.xml
Normal file
33
vector/src/main/res/layout/fragment_room_thread_detail.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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/rootConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/testTextVeiwddasda"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/room_threads_filter"
|
||||
android:textSize="25sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/composerLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<im.vector.app.features.home.room.detail.composer.TextComposerView
|
||||
android:id="@+id/composerLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:minHeight="56dp"
|
||||
android:transitionName="composer"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
9
vector/src/main/res/menu/menu_room_threads.xml
Normal file
9
vector/src/main/res/menu/menu_room_threads.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_filter"
|
||||
android:icon="@drawable/ic_filter"
|
||||
android:title="@string/room_threads_filter"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
@ -1021,6 +1021,9 @@
|
||||
<string name="room_details_people_invited_group_name">INVITED</string>
|
||||
<string name="room_details_people_present_group_name">JOINED</string>
|
||||
|
||||
<!-- Room Threads -->
|
||||
<string name="room_threads_filter">Filter Threads in room</string>
|
||||
|
||||
<!-- Room events -->
|
||||
<string name="room_event_action_report_prompt_reason">Reason for reporting this content</string>
|
||||
<string name="room_event_action_report_prompt_ignore_user">Do you want to hide all messages from this user?\n\nNote that this action will restart the app and it may take some time.</string>
|
||||
@ -2171,6 +2174,7 @@
|
||||
|
||||
<string name="edit">Edit</string>
|
||||
<string name="reply">Reply</string>
|
||||
<string name="reply_in_thread">Reply In Thread</string>
|
||||
|
||||
<string name="global_retry">Retry</string>
|
||||
<string name="room_list_empty">"Join a room to start using the app."</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user