Support both unstable and stable prefixes.
Author: Onuray
This commit is contained in:
parent
2048b859c5
commit
20c1886fed
|
@ -0,0 +1 @@
|
|||
Support both stable and unstable prefixes
|
|
@ -349,7 +349,7 @@ fun Event.isAttachmentMessage(): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
fun Event.isPoll(): Boolean = getClearType() == EventType.POLL_START || getClearType() == EventType.POLL_END
|
||||
fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClearType() in EventType.POLL_END
|
||||
|
||||
fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER
|
||||
|
||||
|
@ -372,7 +372,7 @@ fun Event.getRelationContent(): RelationDefaultContent? {
|
|||
* Returns the poll question or null otherwise
|
||||
*/
|
||||
fun Event.getPollQuestion(): String? =
|
||||
getPollContent()?.pollCreationInfo?.question?.question
|
||||
getPollContent()?.getBestPollCreationInfo()?.question?.getBestQuestion()
|
||||
|
||||
/**
|
||||
* Returns the relation content for a specific type or null otherwise
|
||||
|
|
|
@ -103,9 +103,9 @@ object EventType {
|
|||
const val REACTION = "m.reaction"
|
||||
|
||||
// Poll
|
||||
const val POLL_START = "org.matrix.msc3381.poll.start"
|
||||
const val POLL_RESPONSE = "org.matrix.msc3381.poll.response"
|
||||
const val POLL_END = "org.matrix.msc3381.poll.end"
|
||||
val POLL_START = listOf("org.matrix.msc3381.poll.start", "m.poll.start")
|
||||
val POLL_RESPONSE = listOf("org.matrix.msc3381.poll.response", "m.poll.response")
|
||||
val POLL_END = listOf("org.matrix.msc3381.poll.end", "m.poll.end")
|
||||
|
||||
// Unwedging
|
||||
internal const val DUMMY = "m.dummy"
|
||||
|
|
|
@ -39,37 +39,46 @@ data class MessageLocationContent(
|
|||
*/
|
||||
@Json(name = "geo_uri") val geoUri: String,
|
||||
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
/**
|
||||
* See https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.location") val locationInfo: LocationInfo? = null,
|
||||
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
|
||||
@Json(name = "org.matrix.msc3488.location") val unstableLocationInfo: LocationInfo? = null,
|
||||
@Json(name = "m.location") val locationInfo: LocationInfo? = null,
|
||||
/**
|
||||
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null,
|
||||
@Json(name = "m.ts") val ts: Long? = null,
|
||||
@Json(name = "org.matrix.msc1767.text") val unstableText: String? = null,
|
||||
@Json(name = "m.text") val text: String? = null,
|
||||
/**
|
||||
* m.asset defines a generic asset that can be used for location tracking but also in other places like
|
||||
* inventories, geofencing, checkins/checkouts etc.
|
||||
* It should contain a mandatory namespaced type key defining what particular asset is being referred to.
|
||||
* For the purposes of user location tracking m.self should be used in order to avoid duplicating the mxid.
|
||||
*/
|
||||
@Json(name = "m.asset") val locationAsset: LocationAsset? = null,
|
||||
|
||||
/**
|
||||
* Exact time that the data in the event refers to (milliseconds since the UNIX epoch)
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3488.ts") val ts: Long? = null,
|
||||
|
||||
@Json(name = "org.matrix.msc1767.text") val text: String? = null
|
||||
@Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset? = null,
|
||||
@Json(name = "m.asset") val locationAsset: LocationAsset? = null
|
||||
) : MessageContent {
|
||||
|
||||
fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri
|
||||
fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo
|
||||
|
||||
fun getBestTs() = ts ?: unstableTs
|
||||
|
||||
fun getBestText() = text ?: unstableText
|
||||
|
||||
fun getBestLocationAsset() = locationAsset ?: unstableLocationAsset
|
||||
|
||||
fun getBestGeoUri() = getBestLocationInfo()?.geoUri ?: geoUri
|
||||
|
||||
/**
|
||||
* @return true if the location asset is a user location, not a generic one.
|
||||
*/
|
||||
fun isSelfLocation(): Boolean {
|
||||
// Should behave like m.self if locationAsset is null
|
||||
val locationAsset = getBestLocationAsset()
|
||||
return locationAsset?.type == null || locationAsset.type == LocationAssetType.SELF
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,5 +31,9 @@ data class MessagePollContent(
|
|||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
@Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null
|
||||
) : MessageContent
|
||||
@Json(name = "org.matrix.msc3381.poll.start") val unstablePollCreationInfo: PollCreationInfo? = null,
|
||||
@Json(name = "m.poll.start") val pollCreationInfo: PollCreationInfo? = null
|
||||
) : MessageContent {
|
||||
|
||||
fun getBestPollCreationInfo() = pollCreationInfo ?: unstablePollCreationInfo
|
||||
}
|
||||
|
|
|
@ -31,5 +31,9 @@ data class MessagePollResponseContent(
|
|||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
@Json(name = "org.matrix.msc3381.poll.response") val response: PollResponse? = null
|
||||
) : MessageContent
|
||||
@Json(name = "org.matrix.msc3381.poll.response") val unstableResponse: PollResponse? = null,
|
||||
@Json(name = "m.response") val response: PollResponse? = null
|
||||
) : MessageContent {
|
||||
|
||||
fun getBestResponse() = response ?: unstableResponse
|
||||
}
|
||||
|
|
|
@ -22,5 +22,9 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class PollAnswer(
|
||||
@Json(name = "id") val id: String? = null,
|
||||
@Json(name = "org.matrix.msc1767.text") val answer: String? = null
|
||||
)
|
||||
@Json(name = "org.matrix.msc1767.text") val unstableAnswer: String? = null,
|
||||
@Json(name = "m.text") val answer: String? = null
|
||||
) {
|
||||
|
||||
fun getBestAnswer() = answer ?: unstableAnswer
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PollCreationInfo(
|
||||
@Json(name = "question") val question: PollQuestion? = null,
|
||||
@Json(name = "kind") val kind: PollType? = PollType.DISCLOSED,
|
||||
@Json(name = "max_selections") val maxSelections: Int = 1,
|
||||
@Json(name = "answers") val answers: List<PollAnswer>? = null
|
||||
@Json(name = "question") val question: PollQuestion? = null,
|
||||
@Json(name = "kind") val kind: PollType? = PollType.DISCLOSED_UNSTABLE,
|
||||
@Json(name = "max_selections") val maxSelections: Int = 1,
|
||||
@Json(name = "answers") val answers: List<PollAnswer>? = null
|
||||
)
|
||||
|
|
|
@ -21,5 +21,9 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PollQuestion(
|
||||
@Json(name = "org.matrix.msc1767.text") val question: String? = null
|
||||
)
|
||||
@Json(name = "org.matrix.msc1767.text") val unstableQuestion: String? = null,
|
||||
@Json(name = "m.text") val question: String? = null
|
||||
) {
|
||||
|
||||
fun getBestQuestion() = question ?: unstableQuestion
|
||||
}
|
||||
|
|
|
@ -25,11 +25,17 @@ enum class PollType {
|
|||
* Voters should see results as soon as they have voted.
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3381.poll.disclosed")
|
||||
DISCLOSED_UNSTABLE,
|
||||
|
||||
@Json(name = "m.poll.disclosed")
|
||||
DISCLOSED,
|
||||
|
||||
/**
|
||||
* Results should be only revealed when the poll is ended.
|
||||
*/
|
||||
@Json(name = "org.matrix.msc3381.poll.undisclosed")
|
||||
UNDISCLOSED_UNSTABLE,
|
||||
|
||||
@Json(name = "m.poll.undisclosed")
|
||||
UNDISCLOSED
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ object RoomSummaryConstants {
|
|||
EventType.CALL_ANSWER,
|
||||
EventType.ENCRYPTED,
|
||||
EventType.STICKER,
|
||||
EventType.REACTION,
|
||||
EventType.POLL_START
|
||||
)
|
||||
EventType.REACTION
|
||||
) + EventType.POLL_START
|
||||
}
|
||||
|
|
|
@ -134,9 +134,9 @@ fun TimelineEvent.getEditedEventId(): String? {
|
|||
*/
|
||||
fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
||||
return when (root.getClearType()) {
|
||||
EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>()
|
||||
EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>()
|
||||
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||
EventType.STICKER -> root.getClearContent().toModel<MessageStickerContent>()
|
||||
in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>()
|
||||
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
|
|||
|
||||
val allEvents = (newJoinEvents + inviteEvents).filter { event ->
|
||||
when (event.type) {
|
||||
EventType.POLL_START,
|
||||
in EventType.POLL_START,
|
||||
EventType.MESSAGE,
|
||||
EventType.REDACTION,
|
||||
EventType.ENCRYPTED,
|
||||
|
|
|
@ -86,11 +86,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
// TODO Add ?
|
||||
// EventType.KEY_VERIFICATION_READY,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.ENCRYPTED,
|
||||
EventType.POLL_START,
|
||||
EventType.POLL_RESPONSE,
|
||||
EventType.POLL_END
|
||||
)
|
||||
EventType.ENCRYPTED
|
||||
) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END
|
||||
|
||||
override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean {
|
||||
return allowedTypes.contains(eventType)
|
||||
|
@ -156,7 +153,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
||||
// A replace!
|
||||
handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
||||
} else if (event.getClearType() == EventType.POLL_RESPONSE) {
|
||||
} else if (event.getClearType() in EventType.POLL_RESPONSE) {
|
||||
event.getClearContent().toModel<MessagePollResponseContent>(catchError = true)?.let { pollResponseContent ->
|
||||
Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
|
||||
handleResponse(realm, event, pollResponseContent, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
||||
|
@ -177,12 +174,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
handleVerification(realm, event, roomId, isLocalEcho, it)
|
||||
}
|
||||
}
|
||||
EventType.POLL_RESPONSE -> {
|
||||
in EventType.POLL_RESPONSE -> {
|
||||
event.getClearContent().toModel<MessagePollResponseContent>(catchError = true)?.let {
|
||||
handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId)
|
||||
}
|
||||
}
|
||||
EventType.POLL_END -> {
|
||||
in EventType.POLL_END -> {
|
||||
event.content.toModel<MessageEndPollContent>(catchError = true)?.let {
|
||||
handleEndPoll(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
|
@ -217,7 +214,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
EventType.POLL_START -> {
|
||||
in EventType.POLL_START -> {
|
||||
val content: MessagePollContent? = event.content.toModel()
|
||||
if (content?.relatesTo?.type == RelationType.REPLACE) {
|
||||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
||||
|
@ -225,12 +222,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
handleReplace(realm, event, content, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
EventType.POLL_RESPONSE -> {
|
||||
in EventType.POLL_RESPONSE -> {
|
||||
event.content.toModel<MessagePollResponseContent>(catchError = true)?.let {
|
||||
handleResponse(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
}
|
||||
EventType.POLL_END -> {
|
||||
in EventType.POLL_END -> {
|
||||
event.content.toModel<MessageEndPollContent>(catchError = true)?.let {
|
||||
handleEndPoll(realm, event, it, roomId, isLocalEcho)
|
||||
}
|
||||
|
@ -407,12 +404,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
val option = content.response?.answers?.first() ?: return Unit.also {
|
||||
val option = content.getBestResponse()?.answers?.first() ?: return Unit.also {
|
||||
Timber.d("## POLL Ignoring malformed response no option eventId:$eventId content: ${event.content}")
|
||||
}
|
||||
|
||||
// Check if this option is in available options
|
||||
if (!targetPollContent.pollCreationInfo?.answers?.map { it.id }?.contains(option).orFalse()) {
|
||||
if (!targetPollContent.getBestPollCreationInfo()?.answers?.map { it.id }?.contains(option).orFalse()) {
|
||||
Timber.v("## POLL $targetEventId doesn't contain option $option")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
|
|||
when (typeToPrune) {
|
||||
EventType.ENCRYPTED,
|
||||
EventType.MESSAGE,
|
||||
EventType.POLL_START -> {
|
||||
in EventType.POLL_START -> {
|
||||
Timber.d("REDACTION for message ${eventToPrune.eventId}")
|
||||
val unsignedData = EventMapper.map(eventToPrune).unsignedData
|
||||
?: UnsignedData(null, null)
|
||||
|
|
|
@ -137,16 +137,11 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
options: List<String>,
|
||||
pollType: PollType): MessagePollContent {
|
||||
return MessagePollContent(
|
||||
pollCreationInfo = PollCreationInfo(
|
||||
question = PollQuestion(
|
||||
question = question
|
||||
),
|
||||
unstablePollCreationInfo = PollCreationInfo(
|
||||
question = PollQuestion(unstableQuestion = question),
|
||||
kind = pollType,
|
||||
answers = options.map { option ->
|
||||
PollAnswer(
|
||||
id = UUID.randomUUID().toString(),
|
||||
answer = option
|
||||
)
|
||||
PollAnswer(id = UUID.randomUUID().toString(), unstableAnswer = option)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -167,7 +162,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START,
|
||||
type = EventType.POLL_START.first(),
|
||||
content = newContent.toContent()
|
||||
)
|
||||
}
|
||||
|
@ -179,11 +174,9 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
body = answerId,
|
||||
relatesTo = RelationDefaultContent(
|
||||
type = RelationType.REFERENCE,
|
||||
eventId = pollEventId),
|
||||
response = PollResponse(
|
||||
answers = listOf(answerId)
|
||||
)
|
||||
|
||||
eventId = pollEventId
|
||||
),
|
||||
unstableResponse = PollResponse(answers = listOf(answerId))
|
||||
)
|
||||
val localId = LocalEcho.createLocalEchoId()
|
||||
return Event(
|
||||
|
@ -191,7 +184,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_RESPONSE,
|
||||
type = EventType.POLL_RESPONSE.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
}
|
||||
|
@ -207,7 +200,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_START,
|
||||
type = EventType.POLL_START.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
}
|
||||
|
@ -226,7 +219,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
originServerTs = dummyOriginServerTs(),
|
||||
senderId = userId,
|
||||
eventId = localId,
|
||||
type = EventType.POLL_END,
|
||||
type = EventType.POLL_END.first(),
|
||||
content = content.toContent(),
|
||||
unsignedData = UnsignedData(age = null, transactionId = localId))
|
||||
}
|
||||
|
@ -239,15 +232,10 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
val content = MessageLocationContent(
|
||||
geoUri = geoUri,
|
||||
body = geoUri,
|
||||
locationInfo = LocationInfo(
|
||||
geoUri = geoUri,
|
||||
description = geoUri
|
||||
),
|
||||
locationAsset = LocationAsset(
|
||||
type = LocationAssetType.SELF
|
||||
),
|
||||
ts = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
|
||||
text = geoUri
|
||||
unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri),
|
||||
unstableLocationAsset = LocationAsset(type = LocationAssetType.SELF),
|
||||
unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()),
|
||||
unstableText = geoUri
|
||||
)
|
||||
return createMessageEvent(roomId, content)
|
||||
}
|
||||
|
@ -638,7 +626,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||
MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
|
||||
MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
|
||||
MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
|
||||
MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.pollCreationInfo?.question?.question ?: "")
|
||||
MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "")
|
||||
else -> return TextContent(content?.body ?: "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
|||
|
||||
fun TimelineEvent.canReact(): Boolean {
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
|
||||
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START) &&
|
||||
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START &&
|
||||
root.sendState == SendState.SYNCED &&
|
||||
!root.isRedacted()
|
||||
}
|
||||
|
|
|
@ -1189,7 +1189,7 @@ class TimelineFragment @Inject constructor(
|
|||
val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong())
|
||||
getString(R.string.voice_message_reply_content, formattedDuration)
|
||||
} else if (messageContent is MessagePollContent) {
|
||||
messageContent.pollCreationInfo?.question?.question
|
||||
messageContent.getBestPollCreationInfo()?.question?.getBestQuestion()
|
||||
} else {
|
||||
messageContent?.body ?: ""
|
||||
}
|
||||
|
@ -2165,7 +2165,7 @@ class TimelineFragment @Inject constructor(
|
|||
timelineViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
|
||||
}
|
||||
is EventSharedAction.Edit -> {
|
||||
if (action.eventType == EventType.POLL_START) {
|
||||
if (action.eventType in EventType.POLL_START) {
|
||||
navigator.openCreatePoll(requireContext(), timelineArgs.roomId, action.eventId, PollMode.EDIT)
|
||||
} else if (withState(messageComposerViewModel) { it.isVoiceMessageIdle }) {
|
||||
messageComposerViewModel.handle(MessageComposerAction.EnterEditMode(action.eventId, views.composerLayout.text.toString()))
|
||||
|
|
|
@ -181,7 +181,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
} else {
|
||||
when (timelineEvent.root.getClearType()) {
|
||||
EventType.MESSAGE,
|
||||
EventType.STICKER -> {
|
||||
EventType.STICKER -> {
|
||||
val messageContent: MessageContent? = timelineEvent.getLastMessageContent()
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) {
|
||||
val html = messageContent.formattedBody
|
||||
|
@ -207,13 +207,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
EventType.CALL_INVITE,
|
||||
EventType.CALL_CANDIDATES,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> {
|
||||
EventType.CALL_ANSWER -> {
|
||||
noticeEventFormatter.format(timelineEvent, room?.roomSummary()?.isDirect.orFalse())
|
||||
}
|
||||
EventType.POLL_START -> {
|
||||
timelineEvent.root.getClearContent().toModel<MessagePollContent>(catchError = true)?.pollCreationInfo?.question?.question ?: ""
|
||||
in EventType.POLL_START -> {
|
||||
timelineEvent.root.getClearContent().toModel<MessagePollContent>(catchError = true)?.getBestPollCreationInfo()?.question?.getBestQuestion()
|
||||
?: ""
|
||||
}
|
||||
else -> null
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
|
@ -373,7 +374,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
if (canRedact(timelineEvent, actionPermissions)) {
|
||||
if (timelineEvent.root.getClearType() == EventType.POLL_START) {
|
||||
if (timelineEvent.root.getClearType() in EventType.POLL_START) {
|
||||
add(EventSharedAction.Redact(
|
||||
eventId,
|
||||
askForReason = informationData.senderId != session.myUserId,
|
||||
|
@ -425,7 +426,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only EventType.MESSAGE and EventType.POLL_START event types are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.POLL_START)) return false
|
||||
if (event.root.getClearType() !in EventType.POLL_START + EventType.MESSAGE) return false
|
||||
if (!actionPermissions.canSendMessage) return false
|
||||
return when (messageContent?.msgType) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
|
@ -511,7 +512,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
private fun canRedact(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START)) return false
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START) return false
|
||||
// Message sent by the current user can always be redacted
|
||||
if (event.root.senderId == session.myUserId) return true
|
||||
// Check permission for messages sent by other users
|
||||
|
@ -526,13 +527,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START)) return false
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START) return false
|
||||
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||
}
|
||||
|
||||
private fun canEdit(event: TimelineEvent, myUserId: String, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only event of type EventType.MESSAGE and EventType.POLL_START are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.POLL_START)) return false
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE) + EventType.POLL_START) return false
|
||||
if (!actionPermissions.canSendMessage) return false
|
||||
// TODO if user is admin or moderator
|
||||
val messageContent = event.root.getClearContent().toModel<MessageContent>()
|
||||
|
@ -578,13 +579,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
private fun canEndPoll(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
||||
return event.root.getClearType() == EventType.POLL_START &&
|
||||
return event.root.getClearType() in EventType.POLL_START &&
|
||||
canRedact(event, actionPermissions) &&
|
||||
event.annotations?.pollResponseSummary?.closedTime == null
|
||||
}
|
||||
|
||||
private fun canEditPoll(event: TimelineEvent): Boolean {
|
||||
return event.root.getClearType() == EventType.POLL_START &&
|
||||
return event.root.getClearType() in EventType.POLL_START &&
|
||||
event.annotations?.pollResponseSummary?.closedTime == null &&
|
||||
event.annotations?.pollResponseSummary?.aggregatedContent?.totalVotes ?: 0 == 0
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ class MessageItemFactory @Inject constructor(
|
|||
val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse()
|
||||
val winnerVoteCount = pollResponseSummary?.winnerVoteCount
|
||||
val isPollSent = informationData.sendState.isSent()
|
||||
val isPollUndisclosed = pollContent.pollCreationInfo?.kind == PollType.UNDISCLOSED
|
||||
val isPollUndisclosed = pollContent.getBestPollCreationInfo()?.kind == PollType.UNDISCLOSED_UNSTABLE
|
||||
|
||||
val totalVotesText = (pollResponseSummary?.totalVotes ?: 0).let {
|
||||
when {
|
||||
|
@ -262,13 +262,13 @@ class MessageItemFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
pollContent.pollCreationInfo?.answers?.forEach { option ->
|
||||
pollContent.getBestPollCreationInfo()?.answers?.forEach { option ->
|
||||
val voteSummary = pollResponseSummary?.votes?.get(option.id)
|
||||
val isMyVote = pollResponseSummary?.myVote == option.id
|
||||
val voteCount = voteSummary?.total ?: 0
|
||||
val votePercentage = voteSummary?.percentage ?: 0.0
|
||||
val optionId = option.id ?: ""
|
||||
val optionAnswer = option.answer ?: ""
|
||||
val optionAnswer = option.getBestAnswer() ?: ""
|
||||
|
||||
optionViewStates.add(
|
||||
if (!isPollSent) {
|
||||
|
@ -291,7 +291,7 @@ class MessageItemFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
val question = pollContent.pollCreationInfo?.question?.question ?: ""
|
||||
val question = pollContent.getBestPollCreationInfo()?.question?.getBestQuestion() ?: ""
|
||||
|
||||
return PollItem_()
|
||||
.attributes(attributes)
|
||||
|
|
|
@ -94,7 +94,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
when (event.root.getClearType()) {
|
||||
// Message itemsX
|
||||
EventType.STICKER,
|
||||
EventType.POLL_START,
|
||||
in EventType.POLL_START,
|
||||
EventType.MESSAGE -> messageItemFactory.create(params)
|
||||
EventType.REDACTION,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
|
@ -107,8 +107,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
EventType.CALL_SELECT_ANSWER,
|
||||
EventType.CALL_NEGOTIATE,
|
||||
EventType.REACTION,
|
||||
EventType.POLL_RESPONSE,
|
||||
EventType.POLL_END -> noticeItemFactory.create(params)
|
||||
in EventType.POLL_RESPONSE,
|
||||
in EventType.POLL_END -> noticeItemFactory.create(params)
|
||||
// Calls
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
|
|
|
@ -120,14 +120,14 @@ class DisplayableEventFormatter @Inject constructor(
|
|||
EventType.CALL_CANDIDATES -> {
|
||||
span { }
|
||||
}
|
||||
EventType.POLL_START -> {
|
||||
timelineEvent.root.getClearContent().toModel<MessagePollContent>(catchError = true)?.pollCreationInfo?.question?.question
|
||||
in EventType.POLL_START -> {
|
||||
timelineEvent.root.getClearContent().toModel<MessagePollContent>(catchError = true)?.getBestPollCreationInfo()?.question?.getBestQuestion()
|
||||
?: stringProvider.getString(R.string.sent_a_poll)
|
||||
}
|
||||
EventType.POLL_RESPONSE -> {
|
||||
in EventType.POLL_RESPONSE -> {
|
||||
stringProvider.getString(R.string.poll_response_room_list_preview)
|
||||
}
|
||||
EventType.POLL_END -> {
|
||||
in EventType.POLL_END -> {
|
||||
stringProvider.getString(R.string.poll_end_room_list_preview)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
@ -106,8 +106,8 @@ class NoticeEventFormatter @Inject constructor(
|
|||
EventType.STATE_SPACE_PARENT,
|
||||
EventType.REDACTION,
|
||||
EventType.STICKER,
|
||||
EventType.POLL_RESPONSE,
|
||||
EventType.POLL_END -> formatDebug(timelineEvent.root)
|
||||
in EventType.POLL_RESPONSE,
|
||||
in EventType.POLL_END -> formatDebug(timelineEvent.root)
|
||||
else -> {
|
||||
Timber.v("Type $type not handled by this formatter")
|
||||
null
|
||||
|
@ -196,8 +196,8 @@ class NoticeEventFormatter @Inject constructor(
|
|||
}
|
||||
|
||||
private fun formatDebug(event: Event): CharSequence {
|
||||
val threadPrefix = if (event.isThread()) "thread" else ""
|
||||
return "Debug: $threadPrefix event type \"${event.getClearType()}\""
|
||||
val threadPrefix = if (event.isThread()) "thread" else ""
|
||||
return "Debug: $threadPrefix event type \"${event.getClearType()}\""
|
||||
}
|
||||
|
||||
private fun formatRoomCreateEvent(event: Event, isDm: Boolean): CharSequence? {
|
||||
|
|
|
@ -50,9 +50,8 @@ object TimelineDisplayableEvents {
|
|||
EventType.STATE_ROOM_TOMBSTONE,
|
||||
EventType.STATE_ROOM_JOIN_RULES,
|
||||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
EventType.POLL_START
|
||||
)
|
||||
EventType.KEY_VERIFICATION_CANCEL
|
||||
) + EventType.POLL_START
|
||||
}
|
||||
|
||||
fun TimelineEvent.canBeMerged(): Boolean {
|
||||
|
|
|
@ -43,10 +43,9 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
|
|||
// Can be rendered in bubbles, other types will fallback to default
|
||||
private val EVENT_TYPES_WITH_BUBBLE_LAYOUT = setOf(
|
||||
EventType.MESSAGE,
|
||||
EventType.POLL_START,
|
||||
EventType.ENCRYPTED,
|
||||
EventType.STICKER
|
||||
)
|
||||
) + EventType.POLL_START
|
||||
|
||||
// Can't be rendered in bubbles, so get back to default layout
|
||||
private val MSG_TYPES_WITHOUT_BUBBLE_LAYOUT = setOf(
|
||||
|
|
|
@ -60,9 +60,9 @@ class CreatePollController @Inject constructor(
|
|||
pollTypeChangedListener { _, id ->
|
||||
host.callback?.onPollTypeChanged(
|
||||
if (id == R.id.openPollTypeRadioButton) {
|
||||
PollType.DISCLOSED
|
||||
PollType.DISCLOSED_UNSTABLE
|
||||
} else {
|
||||
PollType.UNDISCLOSED
|
||||
PollType.UNDISCLOSED_UNSTABLE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -71,9 +71,10 @@ class CreatePollViewModel @AssistedInject constructor(
|
|||
val event = room.getTimelineEvent(eventId) ?: return
|
||||
val content = event.getLastMessageContent() as? MessagePollContent ?: return
|
||||
|
||||
val pollType = content.pollCreationInfo?.kind ?: PollType.DISCLOSED
|
||||
val question = content.pollCreationInfo?.question?.question ?: ""
|
||||
val options = content.pollCreationInfo?.answers?.mapNotNull { it.answer } ?: List(MIN_OPTIONS_COUNT) { "" }
|
||||
val pollCreationInfo = content.getBestPollCreationInfo()
|
||||
val pollType = pollCreationInfo?.kind ?: PollType.DISCLOSED_UNSTABLE
|
||||
val question = pollCreationInfo?.question?.getBestQuestion() ?: ""
|
||||
val options = pollCreationInfo?.answers?.mapNotNull { it.getBestAnswer() } ?: List(MIN_OPTIONS_COUNT) { "" }
|
||||
|
||||
setState {
|
||||
copy(
|
||||
|
|
|
@ -27,7 +27,7 @@ data class CreatePollViewState(
|
|||
val options: List<String> = List(CreatePollViewModel.MIN_OPTIONS_COUNT) { "" },
|
||||
val canCreatePoll: Boolean = false,
|
||||
val canAddMoreOptions: Boolean = true,
|
||||
val pollType: PollType = PollType.DISCLOSED
|
||||
val pollType: PollType = PollType.DISCLOSED_UNSTABLE
|
||||
) : MavericksState {
|
||||
|
||||
constructor(args: CreatePollArgs) : this(
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.message.PollType
|
|||
abstract class PollTypeSelectionItem : VectorEpoxyModel<PollTypeSelectionItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var pollType: PollType = PollType.DISCLOSED
|
||||
var pollType: PollType = PollType.DISCLOSED_UNSTABLE
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var pollTypeChangedListener: RadioGroup.OnCheckedChangeListener? = null
|
||||
|
@ -38,8 +38,8 @@ abstract class PollTypeSelectionItem : VectorEpoxyModel<PollTypeSelectionItem.Ho
|
|||
|
||||
holder.pollTypeRadioGroup.check(
|
||||
when (pollType) {
|
||||
PollType.DISCLOSED -> R.id.openPollTypeRadioButton
|
||||
PollType.UNDISCLOSED -> R.id.closedPollTypeRadioButton
|
||||
PollType.DISCLOSED_UNSTABLE, PollType.DISCLOSED -> R.id.openPollTypeRadioButton
|
||||
PollType.UNDISCLOSED_UNSTABLE, PollType.UNDISCLOSED -> R.id.closedPollTypeRadioButton
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -38,8 +38,16 @@ data class ElementWellKnown(
|
|||
val riotE2E: E2EWellKnownConfig? = null,
|
||||
|
||||
@Json(name = "org.matrix.msc3488.tile_server")
|
||||
val mapTileServerConfig: MapTileServerConfig? = null
|
||||
)
|
||||
val unstableMapTileServerConfig: MapTileServerConfig? = null,
|
||||
|
||||
@Json(name = "m.tile_server")
|
||||
val stableMapTileServerConfig: MapTileServerConfig? = null
|
||||
) {
|
||||
|
||||
@Transient
|
||||
var mapTileServerConfig: MapTileServerConfig? = null
|
||||
get() = stableMapTileServerConfig ?: unstableMapTileServerConfig
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class E2EWellKnownConfig(
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.amshove.kluent.shouldBeTrue
|
|||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
|
||||
|
||||
class LocationDataTest {
|
||||
|
@ -64,13 +65,24 @@ class LocationDataTest {
|
|||
|
||||
@Test
|
||||
fun selfLocationTest() {
|
||||
val contentWithNullAsset = MessageLocationContent(body = "", geoUri = "", locationAsset = null)
|
||||
val contentWithNullAsset = MessageLocationContent(body = "", geoUri = "")
|
||||
contentWithNullAsset.isSelfLocation().shouldBeTrue()
|
||||
|
||||
val contentWithNullAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = null))
|
||||
val contentWithNullAssetType = MessageLocationContent(body = "", geoUri = "", unstableLocationAsset = LocationAsset(type = null))
|
||||
contentWithNullAssetType.isSelfLocation().shouldBeTrue()
|
||||
|
||||
val contentWithSelfAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = LocationAssetType.SELF))
|
||||
val contentWithSelfAssetType = MessageLocationContent(body = "", geoUri = "", unstableLocationAsset = LocationAsset(type = LocationAssetType.SELF))
|
||||
contentWithSelfAssetType.isSelfLocation().shouldBeTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unstablePrefixTest() {
|
||||
val geoUri = "geo :12.34,56.78;13.56"
|
||||
|
||||
val contentWithUnstablePrefixes = MessageLocationContent(body = "", geoUri = "", unstableLocationInfo = LocationInfo(geoUri = geoUri))
|
||||
contentWithUnstablePrefixes.getBestLocationInfo()?.geoUri.shouldBeEqualTo(geoUri)
|
||||
|
||||
val contentWithStablePrefixes = MessageLocationContent(body = "", geoUri = "", locationInfo = LocationInfo(geoUri = geoUri))
|
||||
contentWithStablePrefixes.getBestLocationInfo()?.geoUri.shouldBeEqualTo(geoUri)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue