Merge branch 'stable'

This commit is contained in:
Evgeny Poberezkin
2025-01-06 20:18:00 +00:00
6 changed files with 91 additions and 3 deletions

View File

@@ -3503,6 +3503,7 @@ sealed class MsgContent {
@Serializable(with = MsgContentSerializer::class) class MCVideo(override val text: String, val image: String, val duration: Int): MsgContent()
@Serializable(with = MsgContentSerializer::class) class MCVoice(override val text: String, val duration: Int): MsgContent()
@Serializable(with = MsgContentSerializer::class) class MCFile(override val text: String): MsgContent()
@Serializable(with = MsgContentSerializer::class) class MCReport(override val text: String, val reason: ReportReason): MsgContent()
@Serializable(with = MsgContentSerializer::class) class MCUnknown(val type: String? = null, override val text: String, val json: JsonElement): MsgContent()
val isVoice: Boolean get() =
@@ -3579,6 +3580,10 @@ object MsgContentSerializer : KSerializer<MsgContent> {
element("MCFile", buildClassSerialDescriptor("MCFile") {
element<String>("text")
})
element("MCReport", buildClassSerialDescriptor("MCReport") {
element<String>("text")
element<ReportReason>("reason")
})
element("MCUnknown", buildClassSerialDescriptor("MCUnknown"))
}
@@ -3609,6 +3614,10 @@ object MsgContentSerializer : KSerializer<MsgContent> {
MsgContent.MCVoice(text, duration)
}
"file" -> MsgContent.MCFile(text)
"report" -> {
val reason = Json.decodeFromString<ReportReason>(json["reason"].toString())
MsgContent.MCReport(text, reason)
}
else -> MsgContent.MCUnknown(t, text, json)
}
} else {
@@ -3657,6 +3666,12 @@ object MsgContentSerializer : KSerializer<MsgContent> {
put("type", "file")
put("text", value.text)
}
is MsgContent.MCReport ->
buildJsonObject {
put("type", "report")
put("text", value.text)
put("reason", json.encodeToJsonElement(value.reason))
}
is MsgContent.MCUnknown -> value.json
}
encoder.encodeJsonElement(json)
@@ -3751,6 +3766,45 @@ enum class FormatColor(val color: String) {
}
}
@Serializable(with = ReportReasonSerializer::class)
sealed class ReportReason {
@Serializable @SerialName("spam") object Spam: ReportReason()
@Serializable @SerialName("illegal") object Illegal: ReportReason()
@Serializable @SerialName("community") object Community: ReportReason()
@Serializable @SerialName("profile") object Profile: ReportReason()
@Serializable @SerialName("other") object Other: ReportReason()
@Serializable @SerialName("unknown") data class Unknown(val type: String): ReportReason()
}
object ReportReasonSerializer : KSerializer<ReportReason> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("ReportReason", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): ReportReason {
return when (val value = decoder.decodeString()) {
"spam" -> ReportReason.Spam
"illegal" -> ReportReason.Illegal
"community" -> ReportReason.Community
"profile" -> ReportReason.Profile
"other" -> ReportReason.Other
else -> ReportReason.Unknown(value)
}
}
override fun serialize(encoder: Encoder, value: ReportReason) {
val stringValue = when (value) {
is ReportReason.Spam -> "spam"
is ReportReason.Illegal -> "illegal"
is ReportReason.Community -> "community"
is ReportReason.Profile -> "profile"
is ReportReason.Other -> "other"
is ReportReason.Unknown -> value.type
}
encoder.encodeString(stringValue)
}
}
@Serializable
class SndFileTransfer() {}

View File

@@ -978,6 +978,17 @@ object ChatController {
}
}
suspend fun apiReportMessage(rh: Long?, groupId: Long, chatItemId: Long, reportReason: ReportReason, reportText: String): List<AChatItem>? {
val r = sendCmd(rh, CC.ApiReportMessage(groupId, chatItemId, reportReason, reportText))
return when (r) {
is CR.NewChatItems -> r.chatItems
else -> {
apiErrorAlert("apiReportMessage", generalGetString(MR.strings.error_creating_report), r)
null
}
}
}
suspend fun apiGetChatItemInfo(rh: Long?, type: ChatType, id: Long, itemId: Long): ChatItemInfo? {
return when (val r = sendCmd(rh, CC.ApiGetChatItemInfo(type, id, itemId))) {
is CR.ApiChatItemInfo -> r.chatItemInfo
@@ -3202,6 +3213,7 @@ sealed class CC {
class ApiUpdateChatTag(val tagId: Long, val tagData: ChatTagData): CC()
class ApiReorderChatTags(val tagIds: List<Long>): CC()
class ApiCreateChatItems(val noteFolderId: Long, val composedMessages: List<ComposedMessage>): CC()
class ApiReportMessage(val groupId: Long, val chatItemId: Long, val reportReason: ReportReason, val reportText: String): CC()
class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC()
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemIds: List<Long>, val mode: CIDeleteMode): CC()
class ApiDeleteMemberChatItem(val groupId: Long, val itemIds: List<Long>): CC()
@@ -3370,6 +3382,7 @@ sealed class CC {
val msgs = json.encodeToString(composedMessages)
"/_create *$noteFolderId json $msgs"
}
is ApiReportMessage -> "/_report #$groupId $chatItemId reason=$reportReason $reportText"
is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${mc.cmdString}"
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} ${itemIds.joinToString(",")} ${mode.deleteMode}"
is ApiDeleteMemberChatItem -> "/_delete member item #$groupId ${itemIds.joinToString(",")}"
@@ -3533,6 +3546,7 @@ sealed class CC {
is ApiUpdateChatTag -> "apiUpdateChatTag"
is ApiReorderChatTags -> "apiReorderChatTags"
is ApiCreateChatItems -> "apiCreateChatItems"
is ApiReportMessage -> "apiReportMessage"
is ApiUpdateChatItem -> "apiUpdateChatItem"
is ApiDeleteChatItem -> "apiDeleteChatItem"
is ApiDeleteMemberChatItem -> "apiDeleteMemberChatItem"

View File

@@ -170,6 +170,7 @@ fun chatItemPreview(chatItem: ChatItem): ComposePreview {
is MsgContent.MCVideo -> ComposePreview.MediaPreview(images = listOf(mc.image), listOf(UploadContent.SimpleImage(getAppFileUri(fileName))))
is MsgContent.MCVoice -> ComposePreview.VoicePreview(voice = fileName, mc.duration / 1000, true)
is MsgContent.MCFile -> ComposePreview.FilePreview(fileName, getAppFileUri(fileName))
is MsgContent.MCReport -> ComposePreview.NoPreview
is MsgContent.MCUnknown, null -> ComposePreview.NoPreview
}
}
@@ -483,6 +484,7 @@ fun ComposeView(
is MsgContent.MCVideo -> MsgContent.MCVideo(msgText, image = msgContent.image, duration = msgContent.duration)
is MsgContent.MCVoice -> MsgContent.MCVoice(msgText, duration = msgContent.duration)
is MsgContent.MCFile -> MsgContent.MCFile(msgText)
is MsgContent.MCReport -> MsgContent.MCReport(msgText, reason = msgContent.reason)
is MsgContent.MCUnknown -> MsgContent.MCUnknown(type = msgContent.type, text = msgText, json = msgContent.json)
}
}

View File

@@ -139,6 +139,7 @@
<string name="error_sending_message">Error sending message</string>
<string name="error_forwarding_messages">Error forwarding messages</string>
<string name="error_creating_message">Error creating message</string>
<string name="error_creating_report">Error creating report</string>
<string name="error_loading_details">Error loading details</string>
<string name="error_adding_members">Error adding member(s)</string>
<string name="error_joining_group">Error joining group</string>

View File

@@ -3586,7 +3586,7 @@ chatCommandP =
"/_report #" *> (APIReportMessage <$> A.decimal <* A.space <*> A.decimal <*> (" reason=" *> strP) <*> (A.space *> textP <|> pure "")),
"/report #" *> (ReportMessage <$> displayName <*> optional (" @" *> displayName) <*> _strP <* A.space <*> msgTextP),
"/_update item " *> (APIUpdateChatItem <$> chatRefP <* A.space <*> A.decimal <*> liveMessageP <* A.space <*> msgContentP),
"/_delete item " *> (APIDeleteChatItem <$> chatRefP <*> _strP <* A.space <*> ciDeleteMode),
"/_delete item " *> (APIDeleteChatItem <$> chatRefP <*> _strP <*> _strP),
"/_delete member item #" *> (APIDeleteMemberChatItem <$> A.decimal <*> _strP),
"/_reaction " *> (APIChatItemReaction <$> chatRefP <* A.space <*> A.decimal <* A.space <*> onOffP <* A.space <*> jsonP),
"/_reaction members " *> (APIGetReactionMembers <$> A.decimal <* " #" <*> A.decimal <* A.space <*> A.decimal <* A.space <*> jsonP),
@@ -3869,7 +3869,6 @@ chatCommandP =
<|> (PTBefore <$ "before=" <*> strP <* A.space <* "count=" <*> A.decimal)
mcTextP = MCText . safeDecodeUtf8 <$> A.takeByteString
msgContentP = "text " *> mcTextP <|> "json " *> jsonP
ciDeleteMode = "broadcast" $> CIDMBroadcast <|> "internal" $> CIDMInternal
chatDeleteMode =
A.choice
[ " full" *> (CDMFull <$> notifyP),

View File

@@ -17,6 +17,7 @@ module Simplex.Chat.Messages.CIContent where
import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import qualified Data.Aeson.TH as JQ
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.Int (Int64)
import Data.Text (Text)
import Data.Text.Encoding (decodeLatin1, encodeUtf8)
@@ -106,7 +107,24 @@ msgDirectionIntP = \case
data CIDeleteMode = CIDMBroadcast | CIDMInternal | CIDMInternalMark
deriving (Show)
$(JQ.deriveJSON (enumJSON $ dropPrefix "CIDM") ''CIDeleteMode)
instance StrEncoding CIDeleteMode where
strEncode = \case
CIDMBroadcast -> "broadcast"
CIDMInternal -> "internal"
CIDMInternalMark -> "internalMark"
strP =
A.takeTill (== ' ') >>= \case
"broadcast" -> pure CIDMBroadcast
"internal" -> pure CIDMInternal
"internalMark" -> pure CIDMInternalMark
_ -> fail "bad CIDeleteMode"
instance ToJSON CIDeleteMode where
toJSON = strToJSON
toEncoding = strToJEncoding
instance FromJSON CIDeleteMode where
parseJSON = strParseJSON "CIDeleteMode"
ciDeleteModeToText :: CIDeleteMode -> Text
ciDeleteModeToText = \case