forked from GitHub-Mirror/riotX-android
Merge branch 'develop' into feature/better_long_tap_menu
This commit is contained in:
commit
0be987ac0d
@ -2,21 +2,23 @@ Changes in RiotX 0.2.1 (2019-XX-XX)
|
|||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Message Editing: View edit history
|
- Message Editing: View edit history (#121)
|
||||||
- Rooms filtering (#304)
|
- Rooms filtering (#304)
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
- Handle click on redacted events: view source and create permalink
|
- Handle click on redacted events: view source and create permalink
|
||||||
- Improve long tap menu: reply on top, more compact (#368)
|
- Improve long tap menu: reply on top, more compact (#368)
|
||||||
- Quick reply in timeline with swipe gesture
|
- Quick reply in timeline with swipe gesture
|
||||||
|
- Improve edit of replies
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
- migrate from rxbinding 2 to rxbinding 3
|
||||||
|
|
||||||
Bugfix:
|
Bugfix:
|
||||||
- Fix regression on permalink click
|
- Fix regression on permalink click
|
||||||
- Fix crash reported by the PlayStore (#341)
|
- Fix crash reported by the PlayStore (#341)
|
||||||
- Fix Chat composer separator color in dark/black theme
|
- Fix Chat composer separator color in dark/black theme
|
||||||
|
- Fix bad layout for room directory filter (#349)
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
-
|
-
|
||||||
|
15
build.gradle
15
build.gradle
@ -1,3 +1,5 @@
|
|||||||
|
import javax.tools.JavaCompiler
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
@ -52,6 +54,19 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).all {
|
||||||
|
options.compilerArgs += [
|
||||||
|
'-Adagger.gradle.incremental=enabled'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
extensions.findByName("kapt")?.arguments {
|
||||||
|
arg("dagger.gradle.incremental", "enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
@ -57,6 +57,9 @@ interface Session :
|
|||||||
*/
|
*/
|
||||||
val sessionParams: SessionParams
|
val sessionParams: SessionParams
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful shortcut to get access to the userId
|
||||||
|
*/
|
||||||
val myUserId: String
|
val myUserId: String
|
||||||
get() = sessionParams.credentials.userId
|
get() = sessionParams.credentials.userId
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ package im.vector.matrix.android.api.session.events.model
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants defining known event relation types from Matrix specifications.
|
* Constants defining known event relation types from Matrix specifications
|
||||||
*/
|
*/
|
||||||
object RelationType {
|
object RelationType {
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ object RelationType {
|
|||||||
const val ANNOTATION = "m.annotation"
|
const val ANNOTATION = "m.annotation"
|
||||||
/** Lets you define an event which replaces an existing event.*/
|
/** Lets you define an event which replaces an existing event.*/
|
||||||
const val REPLACE = "m.replace"
|
const val REPLACE = "m.replace"
|
||||||
/** ets you define an event which references an existing event.*/
|
/** Lets you define an event which references an existing event.*/
|
||||||
const val REFERENCE = "m.reference"
|
const val REFERENCE = "m.reference"
|
||||||
|
|
||||||
}
|
}
|
@ -25,4 +25,9 @@ interface MessageContent {
|
|||||||
val body: String
|
val body: String
|
||||||
val relatesTo: RelationDefaultContent?
|
val relatesTo: RelationDefaultContent?
|
||||||
val newContent: Content?
|
val newContent: Content?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun MessageContent?.isReply(): Boolean {
|
||||||
|
return this?.relatesTo?.inReplyTo != null
|
||||||
}
|
}
|
@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package im.vector.matrix.android.api.session.room.model.relation
|
package im.vector.matrix.android.api.session.room.model.relation
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||||
|
|
||||||
interface RelationContent {
|
interface RelationContent {
|
||||||
|
/** See [RelationType] for known possible values */
|
||||||
val type: String?
|
val type: String?
|
||||||
val eventId: String?
|
val eventId: String?
|
||||||
val inReplyTo: ReplyToContent?
|
val inReplyTo: ReplyToContent?
|
||||||
|
@ -80,6 +80,22 @@ interface RelationService {
|
|||||||
newBodyAutoMarkdown: Boolean,
|
newBodyAutoMarkdown: Boolean,
|
||||||
compatibilityBodyText: String = "* $newBodyText"): Cancelable
|
compatibilityBodyText: String = "* $newBodyText"): Cancelable
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a reply. This is a special case because replies contains fallback text as a prefix.
|
||||||
|
* This method will take the new body (stripped from fallbacks) and re-add them before sending.
|
||||||
|
* @param replyToEdit The event to edit
|
||||||
|
* @param originalSenderId the sender of the message that this reply (being edited) is relating to
|
||||||
|
* @param originalEventId the event id that this reply (being edited) is relating to
|
||||||
|
* @param newBodyText The edited body (stripped from in reply to content)
|
||||||
|
* @param compatibilityBodyText The text that will appear on clients that don't support yet edition
|
||||||
|
*/
|
||||||
|
fun editReply(replyToEdit: TimelineEvent,
|
||||||
|
originalSenderId: String?,
|
||||||
|
originalEventId : String,
|
||||||
|
newBodyText: String,
|
||||||
|
compatibilityBodyText: String = "* $newBodyText"): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get's the edit history of the given event
|
* Get's the edit history of the given event
|
||||||
*/
|
*/
|
||||||
|
@ -21,7 +21,9 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
|||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.isReply
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This data class is a wrapper around an Event. It allows to get useful data in the context of a timeline.
|
* This data class is a wrapper around an Event. It allows to get useful data in the context of a timeline.
|
||||||
@ -88,3 +90,15 @@ data class TimelineEvent(
|
|||||||
*/
|
*/
|
||||||
fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel()
|
fun TimelineEvent.getLastMessageContent(): MessageContent? = annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
?: root.getClearContent().toModel()
|
?: root.getClearContent().toModel()
|
||||||
|
|
||||||
|
|
||||||
|
fun TimelineEvent.getTextEditableContent(): String? {
|
||||||
|
val originalContent = root.getClearContent().toModel<MessageContent>() ?: return null
|
||||||
|
val isReply = originalContent.isReply()
|
||||||
|
val lastContent = getLastMessageContent()
|
||||||
|
return if (isReply) {
|
||||||
|
return extractUsefulTextFromReply(lastContent?.body ?: "")
|
||||||
|
} else {
|
||||||
|
lastContent?.body ?: ""
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.matrix.android.api.util
|
||||||
|
|
||||||
|
|
||||||
|
object ContentUtils {
|
||||||
|
fun extractUsefulTextFromReply(repliedBody: String): String {
|
||||||
|
val lines = repliedBody.lines()
|
||||||
|
var wellFormed = repliedBody.startsWith(">")
|
||||||
|
var endOfPreviousFound = false
|
||||||
|
val usefullines = ArrayList<String>()
|
||||||
|
lines.forEach {
|
||||||
|
if (it == "") {
|
||||||
|
endOfPreviousFound = true
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
if (!endOfPreviousFound) {
|
||||||
|
wellFormed = wellFormed && it.startsWith(">")
|
||||||
|
} else {
|
||||||
|
usefullines.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usefullines.joinToString("\n").takeIf { wellFormed } ?: repliedBody
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extractUsefulTextFromHtmlReply(repliedBody: String): String {
|
||||||
|
if (repliedBody.startsWith("<mx-reply>")) {
|
||||||
|
val closingTagIndex = repliedBody.lastIndexOf("</mx-reply>")
|
||||||
|
if (closingTagIndex != -1)
|
||||||
|
return repliedBody.substring(closingTagIndex + "</mx-reply>".length).trim()
|
||||||
|
}
|
||||||
|
return repliedBody
|
||||||
|
}
|
||||||
|
}
|
@ -50,16 +50,10 @@ internal class SessionManager @Inject constructor(private val matrixComponent: M
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent {
|
private fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent {
|
||||||
val userId = sessionParams.credentials.userId
|
return sessionComponents.getOrPut(sessionParams.credentials.userId) {
|
||||||
if (sessionComponents.containsKey(userId)) {
|
DaggerSessionComponent
|
||||||
return sessionComponents[userId]!!
|
.factory()
|
||||||
|
.create(matrixComponent, sessionParams)
|
||||||
}
|
}
|
||||||
return DaggerSessionComponent
|
|
||||||
.factory()
|
|
||||||
.create(matrixComponent, sessionParams)
|
|
||||||
.also {
|
|
||||||
sessionComponents[sessionParams.credentials.userId] = it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -94,11 +94,11 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun requireBackgroundSync() {
|
override fun requireBackgroundSync() {
|
||||||
SyncWorker.requireBackgroundSync(context, sessionParams.credentials.userId)
|
SyncWorker.requireBackgroundSync(context, myUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
|
override fun startAutomaticBackgroundSync(repeatDelay: Long) {
|
||||||
SyncWorker.automaticallyBackgroundSync(context, sessionParams.credentials.userId, 0, repeatDelay)
|
SyncWorker.automaticallyBackgroundSync(context, myUserId, 0, repeatDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopAnyBackgroundSync() {
|
override fun stopAnyBackgroundSync() {
|
||||||
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.auth.data.Credentials
|
|||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
@ -132,6 +133,24 @@ internal class DefaultRelationService @Inject constructor(private val context: C
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun editReply(replyToEdit: TimelineEvent,
|
||||||
|
originalSenderId: String?,
|
||||||
|
originalEventId: String,
|
||||||
|
newBodyText: String,
|
||||||
|
compatibilityBodyText: String): Cancelable {
|
||||||
|
val event = eventFactory
|
||||||
|
.createReplaceTextOfReply(roomId,
|
||||||
|
replyToEdit,
|
||||||
|
originalSenderId, originalEventId,
|
||||||
|
newBodyText, true, MessageType.MSGTYPE_TEXT, compatibilityBodyText)
|
||||||
|
.also {
|
||||||
|
saveLocalEcho(it)
|
||||||
|
}
|
||||||
|
val workRequest = createSendEventWork(event)
|
||||||
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
|
return CancelableWork(context, workRequest.id)
|
||||||
|
}
|
||||||
|
|
||||||
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
||||||
val params = FetchEditHistoryTask.Params(roomId, eventId)
|
val params = FetchEditHistoryTask.Params(roomId, eventId)
|
||||||
fetchEditHistoryTask.configureWith(params)
|
fetchEditHistoryTask.configureWith(params)
|
||||||
|
@ -104,6 +104,45 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createReplaceTextOfReply(roomId: String, eventReplaced: TimelineEvent,
|
||||||
|
originalSenderId: String?,
|
||||||
|
originalEventId: String,
|
||||||
|
newBodyText: String,
|
||||||
|
newBodyAutoMarkdown: Boolean,
|
||||||
|
msgType: String,
|
||||||
|
compatibilityText: String): Event {
|
||||||
|
val permalink = PermalinkFactory.createPermalink(roomId, originalEventId)
|
||||||
|
val userLink = originalSenderId?.let { PermalinkFactory.createPermalink(it) } ?: ""
|
||||||
|
|
||||||
|
val body = bodyForReply(eventReplaced.getLastMessageContent(), eventReplaced.root.getClearContent().toModel())
|
||||||
|
val replyFormatted = REPLY_PATTERN.format(
|
||||||
|
permalink,
|
||||||
|
stringProvider.getString(R.string.message_reply_to_prefix),
|
||||||
|
userLink,
|
||||||
|
originalSenderId,
|
||||||
|
body.takeFormatted(),
|
||||||
|
createTextContent(newBodyText, newBodyAutoMarkdown).takeFormatted()
|
||||||
|
)
|
||||||
|
//
|
||||||
|
// > <@alice:example.org> This is the original body
|
||||||
|
//
|
||||||
|
val replyFallback = buildReplyFallback(body, originalSenderId, newBodyText)
|
||||||
|
|
||||||
|
return createEvent(roomId,
|
||||||
|
MessageTextContent(
|
||||||
|
type = msgType,
|
||||||
|
body = compatibilityText,
|
||||||
|
relatesTo = RelationDefaultContent(RelationType.REPLACE, eventReplaced.root.eventId),
|
||||||
|
newContent = MessageTextContent(
|
||||||
|
type = msgType,
|
||||||
|
format = MessageType.FORMAT_MATRIX_HTML,
|
||||||
|
body = replyFallback,
|
||||||
|
formattedBody = replyFormatted
|
||||||
|
)
|
||||||
|
.toContent()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fun createMediaEvent(roomId: String, attachment: ContentAttachmentData): Event {
|
fun createMediaEvent(roomId: String, attachment: ContentAttachmentData): Event {
|
||||||
return when (attachment.type) {
|
return when (attachment.type) {
|
||||||
ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment)
|
ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment)
|
||||||
@ -239,16 +278,8 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
val permalink = PermalinkFactory.createPermalink(eventReplied.root) ?: return null
|
val permalink = PermalinkFactory.createPermalink(eventReplied.root) ?: return null
|
||||||
val userId = eventReplied.root.senderId ?: return null
|
val userId = eventReplied.root.senderId ?: return null
|
||||||
val userLink = PermalinkFactory.createPermalink(userId) ?: return null
|
val userLink = PermalinkFactory.createPermalink(userId) ?: return null
|
||||||
// <mx-reply>
|
|
||||||
// <blockquote>
|
val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.root.getClearContent().toModel())
|
||||||
// <a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
|
|
||||||
// <a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
|
||||||
// <br />
|
|
||||||
// <!-- This is where the related event's HTML would be. -->
|
|
||||||
// </blockquote>
|
|
||||||
// </mx-reply>
|
|
||||||
// This is where the reply goes.
|
|
||||||
val body = bodyForReply(eventReplied.getLastMessageContent())
|
|
||||||
val replyFormatted = REPLY_PATTERN.format(
|
val replyFormatted = REPLY_PATTERN.format(
|
||||||
permalink,
|
permalink,
|
||||||
stringProvider.getString(R.string.message_reply_to_prefix),
|
stringProvider.getString(R.string.message_reply_to_prefix),
|
||||||
@ -260,8 +291,22 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
//
|
//
|
||||||
// > <@alice:example.org> This is the original body
|
// > <@alice:example.org> This is the original body
|
||||||
//
|
//
|
||||||
|
val replyFallback = buildReplyFallback(body, userId, replyText)
|
||||||
|
|
||||||
|
val eventId = eventReplied.root.eventId ?: return null
|
||||||
|
val content = MessageTextContent(
|
||||||
|
type = MessageType.MSGTYPE_TEXT,
|
||||||
|
format = MessageType.FORMAT_MATRIX_HTML,
|
||||||
|
body = replyFallback,
|
||||||
|
formattedBody = replyFormatted,
|
||||||
|
relatesTo = RelationDefaultContent(null, null, ReplyToContent(eventId))
|
||||||
|
)
|
||||||
|
return createEvent(roomId, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String {
|
||||||
val lines = body.text.split("\n")
|
val lines = body.text.split("\n")
|
||||||
val replyFallback = StringBuffer("><$userId>")
|
val replyFallback = StringBuffer("><$originalSenderId>")
|
||||||
lines.forEachIndexed { index, s ->
|
lines.forEachIndexed { index, s ->
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
replyFallback.append(" $s")
|
replyFallback.append(" $s")
|
||||||
@ -269,23 +314,16 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
replyFallback.append("\n>$s")
|
replyFallback.append("\n>$s")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyFallback.append("\n\n").append(replyText)
|
replyFallback.append("\n\n").append(newBodyText)
|
||||||
|
return replyFallback.toString()
|
||||||
val eventId = eventReplied.root.eventId ?: return null
|
|
||||||
val content = MessageTextContent(
|
|
||||||
type = MessageType.MSGTYPE_TEXT,
|
|
||||||
format = MessageType.FORMAT_MATRIX_HTML,
|
|
||||||
body = replyFallback.toString(),
|
|
||||||
formattedBody = replyFormatted,
|
|
||||||
relatesTo = RelationDefaultContent(null, null, ReplyToContent(eventId))
|
|
||||||
)
|
|
||||||
return createEvent(roomId, content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a TextContent used for the fallback event representation in a reply message.
|
* Returns a TextContent used for the fallback event representation in a reply message.
|
||||||
|
* We also pass the original content, because in case of an edit of a reply the last content is not
|
||||||
|
* himself a reply, but it will contain the fallbacks, so we have to trim them.
|
||||||
*/
|
*/
|
||||||
private fun bodyForReply(content: MessageContent?): TextContent {
|
private fun bodyForReply(content: MessageContent?, originalContent: MessageContent?): TextContent {
|
||||||
when (content?.type) {
|
when (content?.type) {
|
||||||
MessageType.MSGTYPE_EMOTE,
|
MessageType.MSGTYPE_EMOTE,
|
||||||
MessageType.MSGTYPE_TEXT,
|
MessageType.MSGTYPE_TEXT,
|
||||||
@ -296,7 +334,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
formattedText = content.formattedBody
|
formattedText = content.formattedBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val isReply = content.relatesTo?.inReplyTo?.eventId != null
|
val isReply = content.isReply() || originalContent.isReply()
|
||||||
return if (isReply)
|
return if (isReply)
|
||||||
TextContent(content.body, formattedText).removeInReplyFallbacks()
|
TextContent(content.body, formattedText).removeInReplyFallbacks()
|
||||||
else
|
else
|
||||||
@ -353,7 +391,16 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
|
|||||||
companion object {
|
companion object {
|
||||||
const val LOCAL_ID_PREFIX = "local."
|
const val LOCAL_ID_PREFIX = "local."
|
||||||
|
|
||||||
// No whitespace
|
|
||||||
|
// <mx-reply>
|
||||||
|
// <blockquote>
|
||||||
|
// <a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
|
||||||
|
// <a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
// <br />
|
||||||
|
// <!-- This is where the related event's HTML would be. -->
|
||||||
|
// </blockquote>
|
||||||
|
// </mx-reply>
|
||||||
|
// No whitespace because currently breaks temporary formatted text to Span
|
||||||
const val REPLY_PATTERN = """<mx-reply><blockquote><a href="%s">%s</a><a href="%s">%s</a><br />%s</blockquote></mx-reply>%s"""
|
const val REPLY_PATTERN = """<mx-reply><blockquote><a href="%s">%s</a><a href="%s">%s</a><br />%s</blockquote></mx-reply>%s"""
|
||||||
|
|
||||||
fun isLocalEchoId(eventId: String): Boolean = eventId.startsWith(LOCAL_ID_PREFIX)
|
fun isLocalEchoId(eventId: String): Boolean = eventId.startsWith(LOCAL_ID_PREFIX)
|
||||||
|
@ -18,6 +18,8 @@ package im.vector.matrix.android.internal.session.room.send
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromHtmlReply
|
||||||
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains a text and eventually a formatted text
|
* Contains a text and eventually a formatted text
|
||||||
@ -47,28 +49,4 @@ fun TextContent.removeInReplyFallbacks(): TextContent {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractUsefulTextFromReply(repliedBody: String): String {
|
|
||||||
val lines = repliedBody.lines()
|
|
||||||
var wellFormed = repliedBody.startsWith(">")
|
|
||||||
var endOfPreviousFound = false
|
|
||||||
val usefullines = ArrayList<String>()
|
|
||||||
lines.forEach {
|
|
||||||
if (it == "") {
|
|
||||||
endOfPreviousFound = true
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
if (!endOfPreviousFound) {
|
|
||||||
wellFormed = wellFormed && it.startsWith(">")
|
|
||||||
} else {
|
|
||||||
usefullines.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return usefullines.joinToString("\n").takeIf { wellFormed } ?: repliedBody
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractUsefulTextFromHtmlReply(repliedBody: String): String {
|
|
||||||
if (repliedBody.startsWith("<mx-reply>")) {
|
|
||||||
return repliedBody.substring(repliedBody.lastIndexOf("</mx-reply>") + "</mx-reply>".length).trim()
|
|
||||||
}
|
|
||||||
return repliedBody
|
|
||||||
}
|
|
||||||
|
@ -187,8 +187,9 @@ dependencies {
|
|||||||
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
||||||
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.0'
|
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.0'
|
||||||
// TODO RxBindings3 exists
|
// RXBinding
|
||||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.2.0'
|
implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'
|
||||||
|
implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.0.0-alpha2'
|
||||||
|
|
||||||
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||||
|
@ -199,7 +199,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
if (eventType == null) {
|
if (eventType == null) {
|
||||||
//Just add a generic unknown event
|
//Just add a generic unknown event
|
||||||
val simpleNotifiableEvent = SimpleNotifiableEvent(
|
val simpleNotifiableEvent = SimpleNotifiableEvent(
|
||||||
session.sessionParams.credentials.userId,
|
session.myUserId,
|
||||||
eventId,
|
eventId,
|
||||||
true, //It's an issue in this case, all event will bing even if expected to be silent.
|
true, //It's an issue in this case, all event will bing even if expected to be silent.
|
||||||
title = getString(R.string.notification_unknown_new_event),
|
title = getString(R.string.notification_unknown_new_event),
|
||||||
@ -238,7 +238,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifiableEvent.isPushGatewayEvent = true
|
notifiableEvent.isPushGatewayEvent = true
|
||||||
notifiableEvent.matrixID = session.sessionParams.credentials.userId
|
notifiableEvent.matrixID = session.myUserId
|
||||||
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
|
||||||
notificationDrawerManager.refreshNotificationDrawer()
|
notificationDrawerManager.refreshNotificationDrawer()
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,15 @@ import im.vector.riotx.features.workers.signout.SignOutViewModel
|
|||||||
interface ViewModelModule {
|
interface ViewModelModule {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewModels with @IntoMap will be injected by this factory
|
||||||
|
*/
|
||||||
@Binds
|
@Binds
|
||||||
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
|
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Below are bindings for the androidx view models (which extend ViewModel). Will be converted to MvRx ViewModel in the future.
|
||||||
|
*/
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(SignOutViewModel::class)
|
@ViewModelKey(SignOutViewModel::class)
|
||||||
@ -110,6 +116,10 @@ interface ViewModelModule {
|
|||||||
@ViewModelKey(ConfigurationViewModel::class)
|
@ViewModelKey(ConfigurationViewModel::class)
|
||||||
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
|
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Below are bindings for the MvRx view models (which extend VectorViewModel). Will be the only usage in the future.
|
||||||
|
*/
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
fun bindHomeActivityViewModelFactory(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory
|
fun bindHomeActivityViewModelFactory(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory
|
||||||
|
|
||||||
|
@ -58,10 +58,10 @@ open class UserAvatarPreference : Preference {
|
|||||||
open fun refreshAvatar() {
|
open fun refreshAvatar() {
|
||||||
val session = mSession ?: return
|
val session = mSession ?: return
|
||||||
val view = mAvatarView ?: return
|
val view = mAvatarView ?: return
|
||||||
session.getUser(session.sessionParams.credentials.userId)?.let {
|
session.getUser(session.myUserId)?.let {
|
||||||
avatarRenderer.render(it, view)
|
avatarRenderer.render(it, view)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
avatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
|
avatarRenderer.render(null, session.myUserId, null, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ import androidx.lifecycle.ViewModel
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.listeners.StepProgressListener
|
import im.vector.matrix.android.api.listeners.StepProgressListener
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.platform.WaitingViewData
|
import im.vector.riotx.core.platform.WaitingViewData
|
||||||
import im.vector.riotx.core.ui.views.KeysBackupBanner
|
import im.vector.riotx.core.ui.views.KeysBackupBanner
|
||||||
@ -57,7 +57,7 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
|
|||||||
keysBackup.restoreKeysWithRecoveryKey(keysVersionResult,
|
keysBackup.restoreKeysWithRecoveryKey(keysVersionResult,
|
||||||
recoveryKey,
|
recoveryKey,
|
||||||
null,
|
null,
|
||||||
session.sessionParams.credentials.userId,
|
session.myUserId,
|
||||||
object : StepProgressListener {
|
object : StepProgressListener {
|
||||||
override fun onStepProgress(step: StepProgressListener.Step) {
|
override fun onStepProgress(step: StepProgressListener.Step) {
|
||||||
when (step) {
|
when (step) {
|
||||||
|
@ -21,8 +21,8 @@ import androidx.lifecycle.ViewModel
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.listeners.StepProgressListener
|
import im.vector.matrix.android.api.listeners.StepProgressListener
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
|
||||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.platform.WaitingViewData
|
import im.vector.riotx.core.platform.WaitingViewData
|
||||||
import im.vector.riotx.core.ui.views.KeysBackupBanner
|
import im.vector.riotx.core.ui.views.KeysBackupBanner
|
||||||
@ -58,7 +58,7 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel
|
|||||||
keysBackup.restoreKeyBackupWithPassword(keysVersionResult,
|
keysBackup.restoreKeyBackupWithPassword(keysVersionResult,
|
||||||
passphrase.value!!,
|
passphrase.value!!,
|
||||||
null,
|
null,
|
||||||
sharedViewModel.session.sessionParams.credentials.userId,
|
sharedViewModel.session.myUserId,
|
||||||
object : StepProgressListener {
|
object : StepProgressListener {
|
||||||
override fun onStepProgress(step: StepProgressListener.Step) {
|
override fun onStepProgress(step: StepProgressListener.Step) {
|
||||||
when (step) {
|
when (step) {
|
||||||
|
@ -52,7 +52,7 @@ class HomeDrawerFragment : VectorBaseFragment() {
|
|||||||
replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer)
|
replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
session.observeUser(session.sessionParams.credentials.userId).observeK(this) { user ->
|
session.observeUser(session.myUserId).observeK(this) { user ->
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView)
|
avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView)
|
||||||
homeDrawerUsernameView.text = user.displayName
|
homeDrawerUsernameView.text = user.displayName
|
||||||
|
@ -93,7 +93,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
|
|||||||
.rx()
|
.rx()
|
||||||
.liveGroupSummaries()
|
.liveGroupSummaries()
|
||||||
.map {
|
.map {
|
||||||
val myUser = session.getUser(session.sessionParams.credentials.userId)
|
val myUser = session.getUser(session.myUserId)
|
||||||
val allCommunityGroup = GroupSummary(
|
val allCommunityGroup = GroupSummary(
|
||||||
groupId = ALL_COMMUNITIES_GROUP_ID,
|
groupId = ALL_COMMUNITIES_GROUP_ID,
|
||||||
displayName = stringProvider.getString(R.string.group_all_communities),
|
displayName = stringProvider.getString(R.string.group_all_communities),
|
||||||
|
@ -62,6 +62,7 @@ import im.vector.matrix.android.api.session.room.model.message.*
|
|||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.getTextEditableContent
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
@ -261,7 +262,7 @@ class RoomDetailFragment :
|
|||||||
composerLayout.composerRelatedMessageContent.text = formattedBody
|
composerLayout.composerRelatedMessageContent.text = formattedBody
|
||||||
?: nonFormattedBody
|
?: nonFormattedBody
|
||||||
|
|
||||||
composerLayout.composerEditText.setText(if (useText) nonFormattedBody else "")
|
composerLayout.composerEditText.setText(if (useText) event.getTextEditableContent() else "")
|
||||||
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
|
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
|
||||||
|
|
||||||
avatarRenderer.render(event.senderAvatar, event.root.senderId
|
avatarRenderer.render(event.senderAvatar, event.root.senderId
|
||||||
@ -515,7 +516,7 @@ class RoomDetailFragment :
|
|||||||
timelineEventController.setTimeline(state.timeline, state.eventId)
|
timelineEventController.setTimeline(state.timeline, state.eventId)
|
||||||
inviteView.visibility = View.GONE
|
inviteView.visibility = View.GONE
|
||||||
|
|
||||||
val uid = session.sessionParams.credentials.userId
|
val uid = session.myUserId
|
||||||
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
|
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
|
||||||
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
|
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
|
||||||
|
|
||||||
@ -809,7 +810,7 @@ class RoomDetailFragment :
|
|||||||
if (null != text) {
|
if (null != text) {
|
||||||
// var vibrate = false
|
// var vibrate = false
|
||||||
|
|
||||||
val myDisplayName = session.getUser(session.sessionParams.credentials.userId)?.displayName
|
val myDisplayName = session.getUser(session.myUserId)?.displayName
|
||||||
if (TextUtils.equals(myDisplayName, text)) {
|
if (TextUtils.equals(myDisplayName, text)) {
|
||||||
// current user
|
// current user
|
||||||
if (TextUtils.isEmpty(composerLayout.composerEditText.text)) {
|
if (TextUtils.isEmpty(composerLayout.composerEditText.text)) {
|
||||||
|
@ -39,7 +39,6 @@ import im.vector.matrix.android.api.session.room.model.message.getFileUrl
|
|||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotx.R
|
|
||||||
import im.vector.riotx.core.intent.getFilenameFromUri
|
import im.vector.riotx.core.intent.getFilenameFromUri
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.resources.UserPreferencesProvider
|
import im.vector.riotx.core.resources.UserPreferencesProvider
|
||||||
@ -52,8 +51,6 @@ import org.commonmark.parser.Parser
|
|||||||
import org.commonmark.renderer.html.HtmlRenderer
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
@ -229,16 +226,24 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is SendMode.EDIT -> {
|
is SendMode.EDIT -> {
|
||||||
val messageContent: MessageContent? =
|
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
|
||||||
val nonFormattedBody = messageContent?.body ?: ""
|
|
||||||
|
|
||||||
if (nonFormattedBody != action.text) {
|
//is original event a reply?
|
||||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
?: "", messageContent?.type ?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
if (inReplyTo != null) {
|
||||||
|
//TODO check if same content?
|
||||||
|
room.editReply(state.sendMode.timelineEvent, room.getTimeLineEvent(inReplyTo)?.root?.senderId, inReplyTo, action.text)
|
||||||
} else {
|
} else {
|
||||||
Timber.w("Same message content, do not send edition")
|
val messageContent: MessageContent? =
|
||||||
|
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
|
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||||
|
val existingBody = messageContent?.body ?: ""
|
||||||
|
if (existingBody != action.text) {
|
||||||
|
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
|
||||||
|
?: "", messageContent?.type
|
||||||
|
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
|
||||||
|
} else {
|
||||||
|
Timber.w("Same message content, do not send edition")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
@ -347,7 +352,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUndoReact(action: RoomDetailActions.UndoReaction) {
|
private fun handleUndoReact(action: RoomDetailActions.UndoReaction) {
|
||||||
room.undoReaction(action.key, action.targetEventId, session.sessionParams.credentials.userId)
|
room.undoReaction(action.key, action.targetEventId, session.myUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -355,7 +360,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
if (action.add) {
|
if (action.add) {
|
||||||
room.sendReaction(action.selectedReaction, action.targetEventId)
|
room.sendReaction(action.selectedReaction, action.targetEventId)
|
||||||
} else {
|
} else {
|
||||||
room.undoReaction(action.selectedReaction, action.targetEventId, session.sessionParams.credentials.userId)
|
room.undoReaction(action.selectedReaction, action.targetEventId, session.myUserId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +132,11 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
|||||||
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
|
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canEdit(event, session.sessionParams.credentials.userId)) {
|
if (canEdit(event, session.myUserId)) {
|
||||||
this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId))
|
this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canRedact(event, session.sessionParams.credentials.userId)) {
|
if (canRedact(event, session.myUserId)) {
|
||||||
this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId))
|
this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
|||||||
}
|
}
|
||||||
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId))
|
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId))
|
||||||
|
|
||||||
if (session.sessionParams.credentials.userId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
|
if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
|
||||||
//not sent by me
|
//not sent by me
|
||||||
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId))
|
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId))
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import com.airbnb.mvrx.Success
|
|||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||||
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.localDateTime
|
import im.vector.riotx.core.extensions.localDateTime
|
||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
@ -60,13 +61,13 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Success -> {
|
is Success -> {
|
||||||
state.editList()?.let { renderEvents(it) }
|
state.editList()?.let { renderEvents(it, state.isOriginalAReply) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderEvents(sourceEvents: List<Event>) {
|
private fun renderEvents(sourceEvents: List<Event>, isOriginalReply: Boolean) {
|
||||||
if (sourceEvents.isEmpty()) {
|
if (sourceEvents.isEmpty()) {
|
||||||
genericItem {
|
genericItem {
|
||||||
id("footer")
|
id("footer")
|
||||||
@ -92,7 +93,7 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastDate = evDate
|
lastDate = evDate
|
||||||
val cContent = getCorrectContent(timelineEvent)
|
val cContent = getCorrectContent(timelineEvent, isOriginalReply)
|
||||||
val body = cContent.second?.let { eventHtmlRenderer.render(it) }
|
val body = cContent.second?.let { eventHtmlRenderer.render(it) }
|
||||||
?: cContent.first
|
?: cContent.first
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
var spannedDiff: Spannable? = null
|
var spannedDiff: Spannable? = null
|
||||||
if (nextEvent != null && cContent.second == null /*No diff for html*/) {
|
if (nextEvent != null && cContent.second == null /*No diff for html*/) {
|
||||||
//compares the body
|
//compares the body
|
||||||
val nContent = getCorrectContent(nextEvent)
|
val nContent = getCorrectContent(nextEvent, isOriginalReply)
|
||||||
val nextBody = nContent.second?.let { eventHtmlRenderer.render(it) }
|
val nextBody = nContent.second?.let { eventHtmlRenderer.render(it) }
|
||||||
?: nContent.first
|
?: nContent.first
|
||||||
val dmp = diff_match_patch()
|
val dmp = diff_match_patch()
|
||||||
@ -144,11 +145,14 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCorrectContent(event: Event): Pair<String, String?> {
|
private fun getCorrectContent(event: Event, isOriginalReply: Boolean): Pair<String, String?> {
|
||||||
val clearContent = event.getClearContent().toModel<MessageTextContent>()
|
val clearContent = event.getClearContent().toModel<MessageTextContent>()
|
||||||
val newContent = clearContent
|
val newContent = clearContent
|
||||||
?.newContent
|
?.newContent
|
||||||
?.toModel<MessageTextContent>()
|
?.toModel<MessageTextContent>()
|
||||||
|
if (isOriginalReply) {
|
||||||
|
return extractUsefulTextFromReply(newContent?.body ?: clearContent?.body ?: "") to null
|
||||||
|
}
|
||||||
return (newContent?.body ?: clearContent?.body ?: "") to (newContent?.formattedBody
|
return (newContent?.body ?: clearContent?.body ?: "") to (newContent?.formattedBody
|
||||||
?: clearContent?.formattedBody)
|
?: clearContent?.formattedBody)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,9 @@ import com.squareup.inject.assisted.AssistedInject
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.isReply
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDateFor
|
|||||||
data class ViewEditHistoryViewState(
|
data class ViewEditHistoryViewState(
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
|
val isOriginalAReply: Boolean = false,
|
||||||
val editList: Async<List<Event>> = Uninitialized)
|
val editList: Async<List<Event>> = Uninitialized)
|
||||||
: MvRxState {
|
: MvRxState {
|
||||||
|
|
||||||
@ -77,11 +81,16 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
|
|||||||
override fun onSuccess(data: List<Event>) {
|
override fun onSuccess(data: List<Event>) {
|
||||||
//TODO until supported by API Add original event manually
|
//TODO until supported by API Add original event manually
|
||||||
val withOriginal = data.toMutableList()
|
val withOriginal = data.toMutableList()
|
||||||
|
var originalIsReply = false
|
||||||
room.getTimeLineEvent(eventId)?.let {
|
room.getTimeLineEvent(eventId)?.let {
|
||||||
withOriginal.add(it.root)
|
withOriginal.add(it.root)
|
||||||
|
originalIsReply = it.root.getClearContent().toModel<MessageContent>().isReply()
|
||||||
}
|
}
|
||||||
setState {
|
setState {
|
||||||
copy(editList = Success(withOriginal))
|
copy(
|
||||||
|
editList = Success(withOriginal),
|
||||||
|
isOriginalAReply = originalIsReply
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -30,10 +30,13 @@ abstract class FilteredRoomFooterItem : VectorEpoxyModel<FilteredRoomFooterItem.
|
|||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var listener: FilteredRoomFooterItemListener? = null
|
var listener: FilteredRoomFooterItemListener? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var currentFilter: String = ""
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
holder.createRoomButton.setOnClickListener { listener?.createRoom() }
|
holder.createRoomButton.setOnClickListener { listener?.createRoom(currentFilter) }
|
||||||
holder.createDirectChat.setOnClickListener { listener?.createDirectChat() }
|
holder.createDirectChat.setOnClickListener { listener?.createDirectChat() }
|
||||||
holder.openRoomDirectory.setOnClickListener { listener?.openRoomDirectory() }
|
holder.openRoomDirectory.setOnClickListener { listener?.openRoomDirectory(currentFilter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
@ -43,7 +46,7 @@ abstract class FilteredRoomFooterItem : VectorEpoxyModel<FilteredRoomFooterItem.
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FilteredRoomFooterItemListener : FabMenuView.Listener {
|
interface FilteredRoomFooterItemListener : FabMenuView.Listener {
|
||||||
fun createRoom()
|
fun createRoom(initialName: String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,17 +20,15 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.replaceFragment
|
import im.vector.riotx.core.extensions.replaceFragment
|
||||||
import im.vector.riotx.core.platform.ToolbarConfigurable
|
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||||
import im.vector.riotx.features.home.room.list.RoomListParams
|
import im.vector.riotx.features.home.room.list.RoomListParams
|
||||||
import kotlinx.android.synthetic.main.activity_filtered_rooms.*
|
import kotlinx.android.synthetic.main.activity_filtered_rooms.*
|
||||||
|
|
||||||
class FilteredRoomsActivity : VectorBaseActivity(), ToolbarConfigurable {
|
class FilteredRoomsActivity : VectorBaseActivity() {
|
||||||
|
|
||||||
private lateinit var roomListFragment: RoomListFragment
|
private lateinit var roomListFragment: RoomListFragment
|
||||||
|
|
||||||
@ -44,6 +42,9 @@ class FilteredRoomsActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
configureToolbar(filteredRoomsToolbar)
|
||||||
|
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
roomListFragment = RoomListFragment.newInstance(RoomListParams(RoomListFragment.DisplayMode.FILTERED))
|
roomListFragment = RoomListFragment.newInstance(RoomListParams(RoomListFragment.DisplayMode.FILTERED))
|
||||||
replaceFragment(roomListFragment, R.id.filteredRoomsFragmentContainer, FRAGMENT_TAG)
|
replaceFragment(roomListFragment, R.id.filteredRoomsFragmentContainer, FRAGMENT_TAG)
|
||||||
@ -67,10 +68,6 @@ class FilteredRoomsActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
filteredRoomsSearchView.requestFocus()
|
filteredRoomsSearchView.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configure(toolbar: Toolbar) {
|
|
||||||
configureToolbar(toolbar)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val FRAGMENT_TAG = "RoomListFragment"
|
private const val FRAGMENT_TAG = "RoomListFragment"
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hide FAB when list is scrolling
|
// Hide FAB when list is scrolling
|
||||||
epoxyRecyclerView.addOnScrollListener(
|
roomListEpoxyRecyclerView.addOnScrollListener(
|
||||||
object : RecyclerView.OnScrollListener() {
|
object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
createChatFabMenu.removeCallbacks(showFabRunnable)
|
createChatFabMenu.removeCallbacks(showFabRunnable)
|
||||||
@ -136,11 +136,14 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun filterRoomsWith(filter: String) {
|
fun filterRoomsWith(filter: String) {
|
||||||
|
// Scroll the list to top
|
||||||
|
roomListEpoxyRecyclerView.scrollToPosition(0)
|
||||||
|
|
||||||
roomListViewModel.accept(RoomListActions.FilterWith(filter))
|
roomListViewModel.accept(RoomListActions.FilterWith(filter))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomDirectory() {
|
override fun openRoomDirectory(initialFilter: String) {
|
||||||
navigator.openRoomDirectory(requireActivity())
|
navigator.openRoomDirectory(requireActivity(), initialFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createDirectChat() {
|
override fun createDirectChat() {
|
||||||
@ -150,12 +153,12 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
|||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val layoutManager = LinearLayoutManager(context)
|
val layoutManager = LinearLayoutManager(context)
|
||||||
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||||
epoxyRecyclerView.layoutManager = layoutManager
|
roomListEpoxyRecyclerView.layoutManager = layoutManager
|
||||||
epoxyRecyclerView.itemAnimator = RoomListAnimator()
|
roomListEpoxyRecyclerView.itemAnimator = RoomListAnimator()
|
||||||
roomController.listener = this
|
roomController.listener = this
|
||||||
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
||||||
stateView.contentView = epoxyRecyclerView
|
stateView.contentView = roomListEpoxyRecyclerView
|
||||||
epoxyRecyclerView.setController(roomController)
|
roomListEpoxyRecyclerView.setController(roomController)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val showFabRunnable = Runnable {
|
private val showFabRunnable = Runnable {
|
||||||
@ -266,9 +269,8 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
|||||||
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Pass title
|
override fun createRoom(initialName: String) {
|
||||||
override fun createRoom() {
|
navigator.openCreateRoom(requireActivity(), initialName)
|
||||||
navigator.openCreateRoom(requireActivity())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -70,13 +70,14 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
|||||||
viewState.rejectingRoomsIds,
|
viewState.rejectingRoomsIds,
|
||||||
viewState.rejectingErrorRoomsIds)
|
viewState.rejectingErrorRoomsIds)
|
||||||
|
|
||||||
addFilterFooter()
|
addFilterFooter(viewState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFilterFooter() {
|
private fun addFilterFooter(viewState: RoomListViewState) {
|
||||||
filteredRoomFooterItem {
|
filteredRoomFooterItem {
|
||||||
id("filter_footer")
|
id("filter_footer")
|
||||||
listener(listener)
|
listener(listener)
|
||||||
|
currentFilter(viewState.roomFilter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class FabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSe
|
|||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
fun createDirectChat()
|
fun createDirectChat()
|
||||||
fun openRoomDirectory()
|
fun openRoomDirectory(initialFilter: String = "")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -22,7 +22,7 @@ import android.os.Bundle
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import com.jakewharton.rxbinding2.widget.RxTextView
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.Authenticator
|
||||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||||
@ -127,9 +127,9 @@ class LoginActivity : VectorBaseActivity() {
|
|||||||
private fun setupAuthButton() {
|
private fun setupAuthButton() {
|
||||||
Observable
|
Observable
|
||||||
.combineLatest(
|
.combineLatest(
|
||||||
RxTextView.textChanges(loginField).map { it.trim().isNotEmpty() },
|
loginField.textChanges().map { it.trim().isNotEmpty() },
|
||||||
RxTextView.textChanges(passwordField).map { it.trim().isNotEmpty() },
|
passwordField.textChanges().map { it.trim().isNotEmpty() },
|
||||||
RxTextView.textChanges(homeServerField).map { it.trim().isNotEmpty() },
|
homeServerField.textChanges().map { it.trim().isNotEmpty() },
|
||||||
Function3<Boolean, Boolean, Boolean, Boolean> { isLoginNotEmpty, isPasswordNotEmpty, isHomeServerNotEmpty ->
|
Function3<Boolean, Boolean, Boolean, Boolean> { isLoginNotEmpty, isPasswordNotEmpty, isHomeServerNotEmpty ->
|
||||||
isLoginNotEmpty && isPasswordNotEmpty && isHomeServerNotEmpty
|
isLoginNotEmpty && isPasswordNotEmpty && isHomeServerNotEmpty
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,13 @@ class DefaultNavigator @Inject constructor() : Navigator {
|
|||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomDirectory(context: Context) {
|
override fun openRoomDirectory(context: Context, initialFilter: String) {
|
||||||
val intent = Intent(context, RoomDirectoryActivity::class.java)
|
val intent = RoomDirectoryActivity.getIntent(context, initialFilter)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openCreateRoom(context: Context) {
|
override fun openCreateRoom(context: Context, initialName: String) {
|
||||||
val intent = CreateRoomActivity.getIntent(context)
|
val intent = CreateRoomActivity.getIntent(context, initialName)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ interface Navigator {
|
|||||||
|
|
||||||
fun openRoomPreview(publicRoom: PublicRoom, context: Context)
|
fun openRoomPreview(publicRoom: PublicRoom, context: Context)
|
||||||
|
|
||||||
fun openCreateRoom(context: Context)
|
fun openCreateRoom(context: Context, initialName: String = "")
|
||||||
|
|
||||||
fun openRoomDirectory(context: Context)
|
fun openRoomDirectory(context: Context, initialFilter: String = "")
|
||||||
|
|
||||||
fun openRoomsFiltering(context: Context)
|
fun openRoomsFiltering(context: Context)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
|||||||
val bodyPreview = event.type
|
val bodyPreview = event.type
|
||||||
|
|
||||||
return SimpleNotifiableEvent(
|
return SimpleNotifiableEvent(
|
||||||
session.sessionParams.credentials.userId,
|
session.myUserId,
|
||||||
eventId = event.eventId!!,
|
eventId = event.eventId!!,
|
||||||
noisy = false,//will be updated
|
noisy = false,//will be updated
|
||||||
timestamp = event.originServerTs ?: System.currentTimeMillis(),
|
timestamp = event.originServerTs ?: System.currentTimeMillis(),
|
||||||
@ -109,7 +109,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
|||||||
roomId = event.root.roomId!!,
|
roomId = event.root.roomId!!,
|
||||||
roomName = roomName)
|
roomName = roomName)
|
||||||
|
|
||||||
notifiableEvent.matrixID = session.sessionParams.credentials.userId
|
notifiableEvent.matrixID = session.myUserId
|
||||||
return notifiableEvent
|
return notifiableEvent
|
||||||
} else {
|
} else {
|
||||||
if (event.root.isEncrypted() && event.root.mxDecryptionResult == null) {
|
if (event.root.isEncrypted() && event.root.mxDecryptionResult == null) {
|
||||||
@ -145,7 +145,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
|||||||
roomName = roomName,
|
roomName = roomName,
|
||||||
roomIsDirect = room.roomSummary()?.isDirect ?: false)
|
roomIsDirect = room.roomSummary()?.isDirect ?: false)
|
||||||
|
|
||||||
notifiableEvent.matrixID = session.sessionParams.credentials.userId
|
notifiableEvent.matrixID = session.myUserId
|
||||||
notifiableEvent.soundName = null
|
notifiableEvent.soundName = null
|
||||||
|
|
||||||
// Get the avatars URL
|
// Get the avatars URL
|
||||||
@ -175,7 +175,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
|
|||||||
val body = noticeEventFormatter.format(event, dName)
|
val body = noticeEventFormatter.format(event, dName)
|
||||||
?: stringProvider.getString(R.string.notification_new_invitation)
|
?: stringProvider.getString(R.string.notification_new_invitation)
|
||||||
return InviteNotifiableEvent(
|
return InviteNotifiableEvent(
|
||||||
session.sessionParams.credentials.userId,
|
session.myUserId,
|
||||||
eventId = event.eventId!!,
|
eventId = event.eventId!!,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
timestamp = event.originServerTs ?: 0,
|
timestamp = event.originServerTs ?: 0,
|
||||||
|
@ -121,9 +121,9 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
|||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
false,
|
false,
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
session.getUser(session.sessionParams.credentials.userId)?.displayName
|
session.getUser(session.myUserId)?.displayName
|
||||||
?: context?.getString(R.string.notification_sender_me),
|
?: context?.getString(R.string.notification_sender_me),
|
||||||
session.sessionParams.credentials.userId,
|
session.myUserId,
|
||||||
message,
|
message,
|
||||||
room.roomId,
|
room.roomId,
|
||||||
room.roomSummary()?.displayName ?: room.roomId,
|
room.roomSummary()?.displayName ?: room.roomId,
|
||||||
|
@ -181,8 +181,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||||||
|
|
||||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
|
|
||||||
val user = session.getUser(session.sessionParams.credentials.userId)
|
val user = session.getUser(session.myUserId)
|
||||||
val myUserDisplayName = user?.displayName ?: session.sessionParams.credentials.userId
|
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
|
||||||
|
val myUserDisplayName = user?.displayName?.takeIf { it.isNotBlank() } ?: session.myUserId
|
||||||
val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE)
|
val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||||
synchronized(eventList) {
|
synchronized(eventList) {
|
||||||
|
|
||||||
@ -343,7 +344,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
|
|||||||
for (event in simpleEvents) {
|
for (event in simpleEvents) {
|
||||||
//We build a simple event
|
//We build a simple event
|
||||||
if (firstTime || !event.hasBeenDisplayed) {
|
if (firstTime || !event.hasBeenDisplayed) {
|
||||||
NotificationUtils.buildSimpleEventNotification(context, event, null, myUserDisplayName)?.let {
|
NotificationUtils.buildSimpleEventNotification(context, event, null, session.myUserId)?.let {
|
||||||
notifications.add(it)
|
notifications.add(it)
|
||||||
NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it)
|
NotificationUtils.showNotificationMessage(context, event.eventId, ROOM_EVENT_NOTIFICATION_ID, it)
|
||||||
event.hasBeenDisplayed = true //we can consider it as displayed
|
event.hasBeenDisplayed = true //we can consider it as displayed
|
||||||
|
@ -204,7 +204,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||||||
var olmVersion = "undefined"
|
var olmVersion = "undefined"
|
||||||
|
|
||||||
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
activeSessionHolder.getSafeActiveSession()?.let { session ->
|
||||||
userId = session.sessionParams.credentials.userId
|
userId = session.myUserId
|
||||||
deviceId = session.sessionParams.credentials.deviceId ?: "undefined"
|
deviceId = session.sessionParams.credentials.deviceId ?: "undefined"
|
||||||
olmVersion = session.getCryptoVersion(context, true)
|
olmVersion = session.getCryptoVersion(context, true)
|
||||||
}
|
}
|
||||||
|
@ -25,14 +25,13 @@ import com.airbnb.epoxy.EpoxyVisibilityTracker
|
|||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.jakewharton.rxbinding2.widget.RxTextView
|
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.themes.ThemeUtils
|
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -70,9 +69,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
|
|||||||
it.setDisplayHomeAsUpEnabled(true)
|
it.setDisplayHomeAsUpEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicRoomsFilter.setBackgroundResource(ThemeUtils.getResourceId(requireContext(), R.drawable.bg_search_edit_text_light))
|
publicRoomsFilter.queryTextChanges()
|
||||||
|
|
||||||
RxTextView.textChanges(publicRoomsFilter)
|
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.subscribeBy {
|
.subscribeBy {
|
||||||
viewModel.filterWith(it.toString())
|
viewModel.filterWith(it.toString())
|
||||||
@ -147,6 +144,11 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
if (publicRoomsFilter.text.toString() != state.currentFilter) {
|
||||||
|
// For initial filter
|
||||||
|
publicRoomsFilter.setText(state.currentFilter)
|
||||||
|
}
|
||||||
|
|
||||||
// Populate list with Epoxy
|
// Populate list with Epoxy
|
||||||
publicRoomsController.setData(state)
|
publicRoomsController.setData(state)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import com.airbnb.mvrx.Uninitialized
|
|||||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||||
|
|
||||||
data class PublicRoomsViewState(
|
data class PublicRoomsViewState(
|
||||||
|
// The current filter
|
||||||
|
val currentFilter: String = "",
|
||||||
// Store cumul of pagination result
|
// Store cumul of pagination result
|
||||||
val publicRooms: List<PublicRoom> = emptyList(),
|
val publicRooms: List<PublicRoom> = emptyList(),
|
||||||
// Current pagination request
|
// Current pagination request
|
||||||
|
@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.roomdirectory
|
package im.vector.riotx.features.roomdirectory
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
|
import com.airbnb.mvrx.viewModel
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.addFragment
|
import im.vector.riotx.core.extensions.addFragment
|
||||||
@ -25,6 +28,7 @@ import im.vector.riotx.core.extensions.addFragmentToBackstack
|
|||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
||||||
|
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomViewModel
|
||||||
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
|
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -39,7 +43,10 @@ class RoomDirectoryActivity : VectorBaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Inject lateinit var createRoomViewModelFactory: CreateRoomViewModel.Factory
|
||||||
@Inject lateinit var roomDirectoryViewModelFactory: RoomDirectoryViewModel.Factory
|
@Inject lateinit var roomDirectoryViewModelFactory: RoomDirectoryViewModel.Factory
|
||||||
|
private val roomDirectoryViewModel: RoomDirectoryViewModel by viewModel()
|
||||||
|
private val createRoomViewModel: CreateRoomViewModel by viewModel()
|
||||||
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.activity_simple
|
override fun getLayoutRes() = R.layout.activity_simple
|
||||||
@ -51,6 +58,11 @@ class RoomDirectoryActivity : VectorBaseActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectoryNavigationViewModel::class.java)
|
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectoryNavigationViewModel::class.java)
|
||||||
|
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
roomDirectoryViewModel.filterWith(intent?.getStringExtra(INITIAL_FILTER) ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
|
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
|
||||||
when (navigation) {
|
when (navigation) {
|
||||||
is Navigation.Back -> onBackPressed()
|
is Navigation.Back -> onBackPressed()
|
||||||
@ -59,6 +71,11 @@ class RoomDirectoryActivity : VectorBaseActivity() {
|
|||||||
is Navigation.Close -> finish()
|
is Navigation.Close -> finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roomDirectoryViewModel.selectSubscribe(this, PublicRoomsViewState::currentFilter) { currentFilter ->
|
||||||
|
// Transmit the filter to the createRoomViewModel
|
||||||
|
createRoomViewModel.setName(currentFilter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
@ -67,4 +84,13 @@ class RoomDirectoryActivity : VectorBaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val INITIAL_FILTER = "INITIAL_FILTER"
|
||||||
|
|
||||||
|
fun getIntent(context: Context, initialFilter: String = ""): Intent {
|
||||||
|
val intent = Intent(context, RoomDirectoryActivity::class.java)
|
||||||
|
intent.putExtra(INITIAL_FILTER, initialFilter)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -59,9 +59,6 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
get() = _joinRoomErrorLiveData
|
get() = _joinRoomErrorLiveData
|
||||||
|
|
||||||
|
|
||||||
// TODO Store in ViewState?
|
|
||||||
private var currentFilter: String = ""
|
|
||||||
|
|
||||||
private var since: String? = null
|
private var since: String? = null
|
||||||
|
|
||||||
private var currentTask: Cancelable? = null
|
private var currentTask: Cancelable? = null
|
||||||
@ -70,9 +67,6 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
private var roomDirectoryData = RoomDirectoryData()
|
private var roomDirectoryData = RoomDirectoryData()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Load with empty filter
|
|
||||||
load()
|
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
roomDirectoryDisplayName = roomDirectoryData.displayName
|
roomDirectoryDisplayName = roomDirectoryData.displayName
|
||||||
@ -115,24 +109,20 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
|
|
||||||
this.roomDirectoryData = roomDirectoryData
|
this.roomDirectoryData = roomDirectoryData
|
||||||
|
|
||||||
reset()
|
reset("")
|
||||||
load()
|
load("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterWith(filter: String) {
|
fun filterWith(filter: String) = withState { state ->
|
||||||
if (currentFilter == filter) {
|
if (state.currentFilter != filter) {
|
||||||
return
|
currentTask?.cancel()
|
||||||
|
|
||||||
|
reset(filter)
|
||||||
|
load(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTask?.cancel()
|
|
||||||
|
|
||||||
currentFilter = filter
|
|
||||||
|
|
||||||
reset()
|
|
||||||
load()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reset() {
|
private fun reset(newFilter: String) {
|
||||||
// Reset since token
|
// Reset since token
|
||||||
since = null
|
since = null
|
||||||
|
|
||||||
@ -141,12 +131,13 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
publicRooms = emptyList(),
|
publicRooms = emptyList(),
|
||||||
asyncPublicRoomsRequest = Loading(),
|
asyncPublicRoomsRequest = Loading(),
|
||||||
hasMore = false,
|
hasMore = false,
|
||||||
roomDirectoryDisplayName = roomDirectoryData.displayName
|
roomDirectoryDisplayName = roomDirectoryData.displayName,
|
||||||
|
currentFilter = newFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadMore() {
|
fun loadMore() = withState { state ->
|
||||||
if (currentTask == null) {
|
if (currentTask == null) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
@ -154,15 +145,15 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
load()
|
load(state.currentFilter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load() {
|
private fun load(filter: String) {
|
||||||
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
||||||
PublicRoomsParams(
|
PublicRoomsParams(
|
||||||
limit = PUBLIC_ROOMS_LIMIT,
|
limit = PUBLIC_ROOMS_LIMIT,
|
||||||
filter = PublicRoomsFilter(searchTerm = currentFilter),
|
filter = PublicRoomsFilter(searchTerm = filter),
|
||||||
includeAllNetworks = roomDirectoryData.includeAllNetworks,
|
includeAllNetworks = roomDirectoryData.includeAllNetworks,
|
||||||
since = since,
|
since = since,
|
||||||
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
|
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
|
||||||
|
@ -21,6 +21,7 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
|
import com.airbnb.mvrx.viewModel
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.addFragment
|
import im.vector.riotx.core.extensions.addFragment
|
||||||
@ -29,12 +30,16 @@ import im.vector.riotx.core.platform.ToolbarConfigurable
|
|||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryNavigationViewModel
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryNavigationViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple container for [CreateRoomFragment]
|
* Simple container for [CreateRoomFragment]
|
||||||
*/
|
*/
|
||||||
class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable {
|
class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
|
@Inject lateinit var createRoomViewModelFactory: CreateRoomViewModel.Factory
|
||||||
|
private val createRoomViewModel: CreateRoomViewModel by viewModel()
|
||||||
|
|
||||||
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.activity_simple
|
override fun getLayoutRes() = R.layout.activity_simple
|
||||||
@ -46,6 +51,8 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
addFragment(CreateRoomFragment(), R.id.simpleFragmentContainer)
|
addFragment(CreateRoomFragment(), R.id.simpleFragmentContainer)
|
||||||
|
|
||||||
|
createRoomViewModel.setName(intent?.getStringExtra(INITIAL_NAME) ?: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,14 +65,19 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||||||
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectoryNavigationViewModel::class.java)
|
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(RoomDirectoryNavigationViewModel::class.java)
|
||||||
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
|
navigationViewModel.navigateTo.observeEvent(this) { navigation ->
|
||||||
when (navigation) {
|
when (navigation) {
|
||||||
is RoomDirectoryActivity.Navigation.Back -> finish()
|
is RoomDirectoryActivity.Navigation.Back,
|
||||||
|
is RoomDirectoryActivity.Navigation.Close -> finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getIntent(context: Context): Intent {
|
private const val INITIAL_NAME = "INITIAL_NAME"
|
||||||
return Intent(context, CreateRoomActivity::class.java)
|
|
||||||
|
fun getIntent(context: Context, initialName: String = ""): Intent {
|
||||||
|
return Intent(context, CreateRoomActivity::class.java).apply {
|
||||||
|
putExtra(INITIAL_NAME, initialName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import android.view.MenuItem
|
|||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
@ -35,9 +35,8 @@ import javax.inject.Inject
|
|||||||
class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
|
class CreateRoomFragment : VectorBaseFragment(), CreateRoomController.Listener {
|
||||||
|
|
||||||
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
private lateinit var navigationViewModel: RoomDirectoryNavigationViewModel
|
||||||
private val viewModel: CreateRoomViewModel by fragmentViewModel()
|
private val viewModel: CreateRoomViewModel by activityViewModel()
|
||||||
@Inject lateinit var createRoomController: CreateRoomController
|
@Inject lateinit var createRoomController: CreateRoomController
|
||||||
@Inject lateinit var createRoomViewModelFactory: CreateRoomViewModel.Factory
|
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_create_room
|
override fun getLayoutResId() = R.layout.fragment_create_room
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.roomdirectory.createroom
|
package im.vector.riotx.features.roomdirectory.createroom
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.airbnb.mvrx.*
|
import com.airbnb.mvrx.*
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
@ -25,6 +26,7 @@ import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
|
|||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
|
import im.vector.matrix.android.api.session.room.model.create.CreateRoomPreset
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||||
|
|
||||||
class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
|
class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateRoomViewState,
|
||||||
private val session: Session
|
private val session: Session
|
||||||
@ -39,8 +41,13 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted initialState: Cr
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? {
|
||||||
val fragment: CreateRoomFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
val activity: FragmentActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||||
return fragment.createRoomViewModelFactory.create(state)
|
|
||||||
|
return when (activity) {
|
||||||
|
is CreateRoomActivity -> activity.createRoomViewModelFactory.create(state)
|
||||||
|
is RoomDirectoryActivity -> activity.createRoomViewModelFactory.create(state)
|
||||||
|
else -> throw IllegalStateException("Wrong activity")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,9 +79,9 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||||||
var oFragment: Fragment? = null
|
var oFragment: Fragment? = null
|
||||||
|
|
||||||
if (VectorPreferences.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
if (VectorPreferences.SETTINGS_NOTIFICATION_TROUBLESHOOT_PREFERENCE_KEY == pref?.key) {
|
||||||
oFragment = VectorSettingsNotificationsTroubleshootFragment.newInstance(session.sessionParams.credentials.userId)
|
oFragment = VectorSettingsNotificationsTroubleshootFragment.newInstance(session.myUserId)
|
||||||
} else if (VectorPreferences.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY == pref?.key) {
|
} else if (VectorPreferences.SETTINGS_NOTIFICATION_ADVANCED_PREFERENCE_KEY == pref?.key) {
|
||||||
oFragment = VectorSettingsAdvancedNotificationPreferenceFragment.newInstance(session.sessionParams.credentials.userId)
|
oFragment = VectorSettingsAdvancedNotificationPreferenceFragment.newInstance(session.myUserId)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
pref?.fragment?.let {
|
pref?.fragment?.let {
|
||||||
|
@ -95,7 +95,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
|||||||
|
|
||||||
// Display name
|
// Display name
|
||||||
mDisplayNamePreference.let {
|
mDisplayNamePreference.let {
|
||||||
it.summary = session.getUser(session.sessionParams.credentials.userId)?.displayName ?: ""
|
it.summary = session.getUser(session.myUserId)?.displayName ?: ""
|
||||||
it.text = it.summary.toString()
|
it.text = it.summary.toString()
|
||||||
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
onDisplayNameClick(newValue?.let { (it as String).trim() })
|
onDisplayNameClick(newValue?.let { (it as String).trim() })
|
||||||
@ -148,7 +148,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() {
|
|||||||
|
|
||||||
// user account
|
// user account
|
||||||
findPreference(VectorPreferences.SETTINGS_LOGGED_IN_PREFERENCE_KEY)
|
findPreference(VectorPreferences.SETTINGS_LOGGED_IN_PREFERENCE_KEY)
|
||||||
.summary = session.sessionParams.credentials.userId
|
.summary = session.myUserId
|
||||||
|
|
||||||
// home server
|
// home server
|
||||||
findPreference(VectorPreferences.SETTINGS_HOME_SERVER_PREFERENCE_KEY)
|
findPreference(VectorPreferences.SETTINGS_HOME_SERVER_PREFERENCE_KEY)
|
||||||
|
@ -367,7 +367,7 @@ class VectorSettingsSecurityPrivacyFragment : VectorSettingsBaseFragment() {
|
|||||||
* @param aMyDeviceInfo the device info
|
* @param aMyDeviceInfo the device info
|
||||||
*/
|
*/
|
||||||
private fun refreshCryptographyPreference(aMyDeviceInfo: DeviceInfo?) {
|
private fun refreshCryptographyPreference(aMyDeviceInfo: DeviceInfo?) {
|
||||||
val userId = session.sessionParams.credentials.userId
|
val userId = session.myUserId
|
||||||
val deviceId = session.sessionParams.credentials.deviceId
|
val deviceId = session.sessionParams.credentials.deviceId
|
||||||
|
|
||||||
// device name
|
// device name
|
||||||
|
@ -35,7 +35,7 @@ class SignOutUiWorker(private val activity: FragmentActivity) {
|
|||||||
activeSessionHolder = context.vectorComponent().activeSessionHolder()
|
activeSessionHolder = context.vectorComponent().activeSessionHolder()
|
||||||
val session = activeSessionHolder.getActiveSession()
|
val session = activeSessionHolder.getActiveSession()
|
||||||
if (SignOutViewModel.doYouNeedToBeDisplayed(session)) {
|
if (SignOutViewModel.doYouNeedToBeDisplayed(session)) {
|
||||||
val signOutDialog = SignOutBottomSheetDialogFragment.newInstance(session.sessionParams.credentials.userId)
|
val signOutDialog = SignOutBottomSheetDialogFragment.newInstance(session.myUserId)
|
||||||
signOutDialog.onSignOut = Runnable {
|
signOutDialog.onSignOut = Runnable {
|
||||||
doSignOut()
|
doSignOut()
|
||||||
}
|
}
|
||||||
|
22
vector/src/main/res/drawable/ic_search.xml
Normal file
22
vector/src/main/res/drawable/ic_search.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M9,9m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#03B381"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19,19l-4.35,-4.35"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#03B381"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -14,17 +14,17 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
|
app:contentInsetStart="0dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
<androidx.appcompat.widget.SearchView
|
||||||
android:id="@+id/filteredRoomsSearchView"
|
android:id="@+id/filteredRoomsSearchView"
|
||||||
|
style="@style/VectorSearchView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:closeIcon="@drawable/ic_x_green"
|
app:queryHint="@string/room_filtering_filter_hint"
|
||||||
app:iconifiedByDefault="false"
|
|
||||||
app:queryHint="@string/home_filter_placeholder_home"
|
|
||||||
app:searchIcon="@drawable/ic_filter" />
|
app:searchIcon="@drawable/ic_filter" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/createRoomToolbar"
|
android:id="@+id/createRoomToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="?actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
app:contentInsetStartWithNavigation="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/groupToolbar"
|
android:id="@+id/groupToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
app:contentInsetStartWithNavigation="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
@ -23,36 +23,22 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/publicRoomsToolbar"
|
android:id="@+id/publicRoomsToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:minHeight="0dp"
|
android:minHeight="0dp"
|
||||||
app:contentInsetStartWithNavigation="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways">
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways">
|
||||||
|
|
||||||
<!-- Note: Background is modified in the code for other themes -->
|
<androidx.appcompat.widget.SearchView
|
||||||
<EditText
|
|
||||||
android:id="@+id/publicRoomsFilter"
|
android:id="@+id/publicRoomsFilter"
|
||||||
|
style="@style/VectorSearchView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="32dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
app:queryHint="@string/room_directory_search_hint" />
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginRight="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="@drawable/bg_search_edit_text_light"
|
|
||||||
android:drawableStart="@drawable/ic_search_white"
|
|
||||||
android:drawableLeft="@drawable/ic_search_white"
|
|
||||||
android:drawablePadding="8dp"
|
|
||||||
android:drawableTint="?riotx_text_secondary"
|
|
||||||
android:hint="@string/home_filter_placeholder_rooms"
|
|
||||||
android:lines="1"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingRight="8dp" />
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/roomToolbar"
|
android:id="@+id/roomToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="?actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
app:contentInsetStartWithNavigation="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
android:background="?riotx_header_panel_background">
|
android:background="?riotx_header_panel_background">
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
android:id="@+id/epoxyRecyclerView"
|
android:id="@+id/roomListEpoxyRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/roomPreviewNoPreviewToolbar"
|
android:id="@+id/roomPreviewNoPreviewToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?actionBarSize"
|
android:layout_height="?actionBarSize"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp">
|
||||||
app:contentInsetStartWithNavigation="0dp">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -26,11 +26,14 @@
|
|||||||
<string name="no_message_edits_found">No edits found</string>
|
<string name="no_message_edits_found">No edits found</string>
|
||||||
|
|
||||||
<!-- Room filtering -->
|
<!-- Room filtering -->
|
||||||
|
<string name="room_filtering_filter_hint">Filter conversations…</string>
|
||||||
<string name="room_filtering_footer_title">Can’t find what you’re looking for?</string>
|
<string name="room_filtering_footer_title">Can’t find what you’re looking for?</string>
|
||||||
<string name="room_filtering_footer_create_new_room">Create a new room</string>
|
<string name="room_filtering_footer_create_new_room">Create a new room</string>
|
||||||
<string name="room_filtering_footer_create_new_direct_message">Send a new direct message</string>
|
<string name="room_filtering_footer_create_new_direct_message">Send a new direct message</string>
|
||||||
<string name="room_filtering_footer_open_room_directory">View the room directory</string>
|
<string name="room_filtering_footer_open_room_directory">View the room directory</string>
|
||||||
|
|
||||||
|
<string name="room_directory_search_hint">Name or ID (#example:matrix.org)</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="labs_swipe_to_reply_in_timeline">Enable swipe to reply in timeline</string>
|
<string name="labs_swipe_to_reply_in_timeline">Enable swipe to reply in timeline</string>
|
||||||
</resources>
|
</resources>
|
@ -9,6 +9,7 @@
|
|||||||
<item name="titleTextAppearance">@style/Vector.Toolbar.Title</item>
|
<item name="titleTextAppearance">@style/Vector.Toolbar.Title</item>
|
||||||
<item name="subtitleTextAppearance">@style/Vector.Toolbar.SubTitle</item>
|
<item name="subtitleTextAppearance">@style/Vector.Toolbar.SubTitle</item>
|
||||||
<item name="android:background">?riotx_background</item>
|
<item name="android:background">?riotx_background</item>
|
||||||
|
<item name="contentInsetStartWithNavigation">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="VectorToolbarStyle.Group">
|
<style name="VectorToolbarStyle.Group">
|
||||||
@ -160,6 +161,12 @@
|
|||||||
<item name="colorControlHighlight">@android:color/white</item>
|
<item name="colorControlHighlight">@android:color/white</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="VectorSearchView" parent="Widget.AppCompat.SearchView">
|
||||||
|
<item name="searchIcon">@drawable/ic_search</item>
|
||||||
|
<item name="closeIcon">@drawable/ic_x_green</item>
|
||||||
|
<item name="iconifiedByDefault">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="VectorSearches.EditText" parent="Widget.AppCompat.EditText">
|
<style name="VectorSearches.EditText" parent="Widget.AppCompat.EditText">
|
||||||
<item name="android:textCursorDrawable">@drawable/searches_cursor_background</item>
|
<item name="android:textCursorDrawable">@drawable/searches_cursor_background</item>
|
||||||
<item name="android:background">@android:color/transparent</item>
|
<item name="android:background">@android:color/transparent</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user