リアクション機能の改善

This commit is contained in:
tateisu 2021-05-27 14:54:04 +09:00
parent 44b1a5b54a
commit abef450f8f
16 changed files with 244 additions and 141 deletions

View File

@ -636,7 +636,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
btnNotificationStyleEditReply,
).forEach { it.isEnabledAlpha = enabledNewNotification }
val ti = TootInstance.getCached(a.apiHost)
val ti = TootInstance.getCached(a)
if (ti == null) {
etMediaSizeMax.setText(a.image_max_megabytes ?: "")
etMovieSizeMax.setText(a.movie_max_megabytes ?: "")

View File

@ -1318,7 +1318,7 @@ class ActPost : AppCompatActivity(),
val account = account
if (account != null && !account.isPseudo) {
// インスタンス情報を確認する
val info = TootInstance.getCached(account.apiHost)
val info = TootInstance.getCached(account)
if (info == null || info.isExpired) {
// 情報がないか古いなら再取得
@ -2137,7 +2137,7 @@ class ActPost : AppCompatActivity(),
return
}
val instance = TootInstance.getCached(account.apiHost)
val instance = TootInstance.getCached(account)
if (instance?.instanceType == InstanceType.Pixelfed) {
if (in_reply_to_id != null) {
showToast(true, R.string.pixelfed_does_not_allow_reply_with_media)
@ -2580,7 +2580,7 @@ class ActPost : AppCompatActivity(),
}
private fun performVisibility() {
val ti = account?.let { TootInstance.getCached(it.apiHost) }
val ti = TootInstance.getCached(account)
val list = when {
account?.isMisskey == true ->

View File

@ -252,6 +252,7 @@ fun Column.isFiltered(item: TootNotification): Boolean {
TootNotification.TYPE_MENTION,
TootNotification.TYPE_REPLY -> dont_show_reply
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION -> dont_show_reaction
@ -278,6 +279,7 @@ fun Column.isFiltered(item: TootNotification): Boolean {
TootNotification.TYPE_MENTION,
TootNotification.TYPE_REPLY -> quick_filter != Column.QUICK_FILTER_MENTION
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION -> quick_filter != Column.QUICK_FILTER_REACTION
@ -318,6 +320,7 @@ fun Column.isFiltered(item: TootNotification): Boolean {
TootNotification.TYPE_RENOTE,
TootNotification.TYPE_QUOTE,
TootNotification.TYPE_FAVOURITE,
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION,
TootNotification.TYPE_FOLLOW,

View File

@ -16,13 +16,12 @@ import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.util.*
import jp.juggler.util.*
import org.jetbrains.anko.allCaps
import org.jetbrains.anko.dip
fun ItemViewHolder.makeReactionsView(status: TootStatus) {
val myReaction = status.reactionSet?.myReaction
val reactionSet = status.reactionSet?.filter { it.count > 0 }
if (reactionSet?.isEmpty() != false) {
if (!TootReaction.canReaction(access_info) || !Pref.bpKeepReactionSpace(activity.pref)) return
val reactionSet = status.reactionSet
if ( reactionSet?.hasReaction() != true ){
if(!TootReaction.canReaction(access_info) || !Pref.bpKeepReactionSpace(activity.pref)) return
}
val density = activity.density
@ -66,6 +65,9 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
)
reactionSet?.forEachIndexed { index, reaction ->
if( reaction.count <= 0 ) return@forEachIndexed
val ssb = reaction.toSpannableStringBuilder(options, status)
.also { it.append(" ${reaction.count}") }
@ -75,10 +77,11 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
buttonHeight
).apply {
if (index > 0) startMargin = marginBetween
bottomMargin = dip(3)
}
minWidthCompat = buttonHeight
background = if (reaction == myReaction) {
background = if (reactionSet.isMyReaction(reaction) ) {
// 自分がリアクションしたやつは背景を変える
getAdaptiveRippleDrawableRound(
act,
@ -102,8 +105,8 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
tag = reaction
setOnClickListener {
val taggedReaction = it.tag as? TootReaction
if (taggedReaction == status.reactionSet?.myReaction) {
act.reactionRemove( column, status)
if ( status.reactionSet?.isMyReaction(taggedReaction) == true ) {
act.reactionRemove( column, status,taggedReaction)
} else {
act.reactionAdd( column, status, taggedReaction?.name, taggedReaction?.static_url)
}
@ -126,7 +129,6 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
box.addView(b)
}
llExtra.addView(box)
}

View File

@ -513,6 +513,7 @@ fun ItemViewHolder.showNotification(n: TootNotification) {
}
}
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION -> {
val colorBg = Pref.ipEventBgColorReaction(activity.pref)

View File

@ -185,7 +185,7 @@ class StatusButtons(
)
}
val ti = TootInstance.getCached(access_info.apiHost)
val ti = TootInstance.getCached(access_info)
btnQuote.vg(ti?.feature_quote == true)?.let {
setButton(
it,
@ -197,17 +197,19 @@ class StatusButtons(
}
btnReaction.vg(TootReaction.canReaction(access_info, ti))?.let {
val myReaction = status.reactionSet?.myReaction
val canMultipleReaction = InstanceCapability.canMultipleReaction(access_info,ti)
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
val bRemoveButton = hasMyReaction &&!canMultipleReaction
setButton(
it,
true,
color_normal,
if (myReaction != null)
if (bRemoveButton)
R.drawable.ic_remove
else
R.drawable.ic_add,
activity.getString(
if (myReaction != null)
if (bRemoveButton)
R.string.reaction_remove
else
R.string.reaction_add
@ -555,17 +557,21 @@ class StatusButtons(
}
}
btnReaction -> when {
!TootReaction.canReaction(access_info) ->
activity.reactionFromAnotherAccount(
access_info,
status
)
status.reactionSet?.myReaction != null ->
activity.reactionRemove(column, status)
else ->
activity.reactionAdd(column, status)
btnReaction ->{
val canMultipleReaction = InstanceCapability.canMultipleReaction(access_info)
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
val bRemoveButton = hasMyReaction &&!canMultipleReaction
when {
!TootReaction.canReaction(access_info) ->
activity.reactionFromAnotherAccount(
access_info,
status
)
bRemoveButton ->
activity.reactionRemove(column, status)
else ->
activity.reactionAdd(column, status)
}
}

View File

@ -3,9 +3,7 @@ package jp.juggler.subwaytooter.action
import androidx.appcompat.app.AlertDialog
import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.Host
import jp.juggler.subwaytooter.api.entity.TootReaction
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.DlgConfirm
import jp.juggler.subwaytooter.dialog.EmojiPicker
import jp.juggler.subwaytooter.dialog.pickAccount
@ -16,6 +14,12 @@ import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.DecodeOptions
import jp.juggler.util.*
private val rePleromaStatusUrl = """/objects/""".toRegex()
private fun String.likePleromaStatusUrl(): Boolean {
return rePleromaStatusUrl.find(this) != null
}
// 長押しでない普通のリアクション操作
fun ActMain.reactionAdd(
column: Column,
@ -30,14 +34,15 @@ fun ActMain.reactionAdd(
isConfirmed: Boolean = false
) {
val activity = this@reactionAdd
val access_info = column.access_info
if (status.reactionSet?.myReaction != null) {
val canMultipleReaction = InstanceCapability.canMultipleReaction(access_info)
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
if (hasMyReaction && !canMultipleReaction) {
showToast(false, R.string.already_reactioned)
return
}
val access_info = column.access_info
var code = codeArg
if (code == null) {
EmojiPicker(activity, access_info, closeOnSelected = true) { result ->
@ -80,6 +85,11 @@ fun ActMain.reactionAdd(
}
}
if (canMultipleReaction && TootReaction.isCustomEmoji(code)) {
showToast(false, "can't reaction with custom emoji from this account")
return
}
if (!isConfirmed) {
val options = DecodeOptions(
activity,
@ -91,7 +101,7 @@ fun ActMain.reactionAdd(
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
DlgConfirm.open(
activity,
activity.getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(access_info)),
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(access_info)),
object : DlgConfirm.Callback {
override var isConfirmEnabled: Boolean
get() = access_info.confirm_reaction
@ -116,21 +126,30 @@ fun ActMain.reactionAdd(
launchMain {
var resultStatus: TootStatus? = null
runApiTask(access_info) { client ->
if (access_info.isMisskey) {
client.request("/api/notes/reactions/create", access_info.putMisskeyApiToken().apply {
put("noteId", status.id.toString())
put("reaction", code)
}.toPostRequestBuilder())
// 成功すると204 no content
} else {
client.request(
when {
access_info.isMisskey -> client.request(
"/api/notes/reactions/create",
access_info.putMisskeyApiToken().apply {
put("noteId", status.id.toString())
put("reaction", code)
}.toPostRequestBuilder()
) // 成功すると204 no content
canMultipleReaction -> client.request(
"/api/v1/pleroma/statuses/${status.id}/reactions/${code.encodePercent("@")}",
"".toFormRequestBody().toPut()
)?.also { result ->
// 成功すると新しいステータス
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
else -> client.request(
"/api/v1/statuses/${status.id}/emoji_reactions/${code.encodePercent("@")}",
"".toFormRequestBody().toPut()
)
)?.also { result ->
// 成功すると新しいステータス
?.also { result ->
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
}
}?.let { result ->
@ -169,51 +188,66 @@ fun ActMain.reactionAdd(
fun ActMain.reactionRemove(
column: Column,
status: TootStatus,
reactionArg: TootReaction? = null,
confirmed: Boolean = false
) {
val activity = this
val access_info = column.access_info
val myReaction = status.reactionSet?.myReaction
val canMultipleReaction = InstanceCapability.canMultipleReaction(access_info)
if (myReaction == null) {
// 指定されたリアクションまたは自分がリアクションした最初のもの
val reaction = reactionArg ?: status.reactionSet?.find { it.count > 0 && it.me }
if (reaction == null) {
showToast(false, R.string.not_reactioned)
return
}
if (!confirmed) {
AlertDialog.Builder(this)
.setMessage(getString(R.string.reaction_remove_confirm, myReaction.name))
val options = DecodeOptions(
activity,
access_info,
decodeEmoji = true,
enlargeEmoji = 1.5f,
enlargeCustomEmoji = 1.5f
)
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
AlertDialog.Builder(activity)
.setMessage(getString(R.string.reaction_remove_confirm, emojiSpan))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
reactionRemove(column, status, confirmed = true)
reactionRemove(column, status, reaction, confirmed = true)
}
.show()
return
}
launchMain {
val activity = this@reactionRemove
var resultStatus: TootStatus? = null
runApiTask(access_info) { client ->
if (access_info.isMisskey) {
client.request(
when {
access_info.isMisskey -> client.request(
"/api/notes/reactions/delete",
access_info.putMisskeyApiToken().apply {
put("noteId", status.id.toString())
}
.toPostRequestBuilder()
)
// 成功すると204 no content
} else {
client.request(
}.toPostRequestBuilder()
) // 成功すると204 no content
canMultipleReaction -> client.request(
"/api/v1/pleroma/statuses/${status.id}/reactions/${reaction.name.encodePercent("@")}",
"".toFormRequestBody().toDelete()
)?.also { result ->
// 成功すると新しいステータス
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
else -> client.request(
"/api/v1/statuses/${status.id}/emoji_unreaction",
"".toFormRequestBody().toPost()
)
)?.also { result ->
// 成功すると新しいステータス
?.also { result ->
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
resultStatus = TootParser(activity, access_info).status(result.jsonObject)
}
}
}?.let { result ->
val resCode = result.response?.code
@ -225,12 +259,9 @@ fun ActMain.reactionRemove(
else -> {
when (val newStatus = resultStatus) {
null ->
if (status.decreaseReactionMisskey(myReaction.name, true, "removeReaction")) {
if (status.decreaseReactionMisskey(reaction.name, true, "removeReaction")) {
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
column.fireShowContent(
reason = "removeReaction complete",
reset = true
)
column.fireShowContent(reason = "removeReaction complete", reset = true)
}
else ->
@ -255,8 +286,10 @@ private fun ActMain.reactionWithoutUi(
isConfirmed: Boolean = false,
callback: () -> Unit,
) {
val activity = this
if (reactionCode == null) {
EmojiPicker(this, access_info, closeOnSelected = true) { result ->
EmojiPicker(activity, access_info, closeOnSelected = true) { result ->
var newUrl: String? = null
val newCode = when (val emoji = result.emoji) {
is UnicodeEmoji -> emoji.unifiedCode
@ -281,62 +314,93 @@ private fun ActMain.reactionWithoutUi(
return
}
val canMultipleReaction = InstanceCapability.canMultipleReaction(access_info)
if (!isConfirmed) {
val options = DecodeOptions(
this,
activity,
access_info,
decodeEmoji = true,
enlargeEmoji = 1.5f,
enlargeCustomEmoji = 1.5f
)
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
DlgConfirm.open(
this,
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(access_info)),
object : DlgConfirm.Callback {
override var isConfirmEnabled: Boolean
get() = access_info.confirm_reaction
set(bv) {
access_info.confirm_reaction = bv
access_info.saveSetting()
}
override fun onOK() {
reactionWithoutUi(
access_info = access_info,
resolvedStatus = resolvedStatus,
reactionCode = reactionCode,
reactionImage = reactionImage,
isConfirmed = true,
callback = callback
)
}
})
val isCustomEmoji = TootReaction.isCustomEmoji(reactionCode)
val url = resolvedStatus.url
when {
isCustomEmoji && canMultipleReaction -> {
showToast(false, "can't reaction with custom emoji from this account")
return
}
isCustomEmoji && url?.likePleromaStatusUrl() == true -> DlgConfirm.openSimple(
activity,
getString(
R.string.confirm_reaction_to_pleroma,
emojiSpan,
AcctColor.getNickname(access_info),
resolvedStatus.account.acct.host?.pretty ?: "(null)"
),
) {
reactionWithoutUi(
access_info = access_info,
resolvedStatus = resolvedStatus,
reactionCode = reactionCode,
reactionImage = reactionImage,
isConfirmed = true,
callback = callback
)
}
else -> DlgConfirm.open(
activity,
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(access_info)),
object : DlgConfirm.Callback {
override var isConfirmEnabled: Boolean
get() = access_info.confirm_reaction
set(bv) {
access_info.confirm_reaction = bv
access_info.saveSetting()
}
override fun onOK() {
reactionWithoutUi(
access_info = access_info,
resolvedStatus = resolvedStatus,
reactionCode = reactionCode,
reactionImage = reactionImage,
isConfirmed = true,
callback = callback
)
}
})
}
return
}
launchMain {
// var resultStatus: TootStatus? = null
runApiTask(access_info) { client ->
if (access_info.isMisskey) {
client.request(
when {
access_info.isMisskey -> client.request(
"/api/notes/reactions/create",
access_info.putMisskeyApiToken().apply {
put("noteId", resolvedStatus.id.toString())
put("reaction", reactionCode)
}.toPostRequestBuilder()
) // 成功すると204 no content
}
.toPostRequestBuilder()
)
// 成功すると204 no content
} else {
client.request(
canMultipleReaction -> client.request(
"/api/v1/pleroma/statuses/${resolvedStatus.id}/reactions/${reactionCode.encodePercent("@")}",
"".toFormRequestBody().toPut()
) // 成功すると更新された投稿
else -> client.request(
"/api/v1/statuses/${resolvedStatus.id}/emoji_reactions/${reactionCode.encodePercent()}",
"".toFormRequestBody().toPut()
)
// ?.also { _ ->
// // 成功すると更新された投稿
// resultStatus = TootParser(activity, access_info).status(result.jsonObject)
// }
) // 成功すると更新された投稿
}
}?.let { result ->
when (val error = result.error) {
@ -404,6 +468,7 @@ fun ActMain.reactionFromAnotherAccount(
reaction: TootReaction? = null,
) {
statusArg ?: return
val activity = this
fun afterResolveStatus(actionAccount: SavedAccount, resolvedStatus: TootStatus) {
val code = if (reaction == null) {
@ -425,7 +490,6 @@ fun ActMain.reactionFromAnotherAccount(
}
launchMain {
val activity = this@reactionFromAnotherAccount
val list = accountListCanReaction() ?: return@launchMain
if (list.isEmpty()) {
@ -442,15 +506,11 @@ fun ActMain.reactionFromAnotherAccount(
afterResolveStatus(action_account, statusArg)
} else {
var newStatus: TootStatus? = null
runApiTask (action_account,progressStyle = ApiTask.PROGRESS_NONE ){ client->
runApiTask(action_account, progressStyle = ApiTask.PROGRESS_NONE) { client ->
val (result, status) = client.syncStatus(action_account, statusArg)
if (status?.reactionSet?.myReaction != null) {
return@runApiTask TootApiResult(activity.getString(R.string.already_reactioned))
}else {
newStatus = status
result
}
}?.let{result->
newStatus = status
result
}?.let { result ->
result.error?.let {
activity.showToast(true, it)
return@launchMain

View File

@ -54,7 +54,16 @@ object InstanceCapability {
when {
ai.isPseudo -> false
ai.isMisskey -> true
else -> ti?.fedibird_capabilities?.contains("emoji_reaction") == true
else ->
ti?.fedibird_capabilities?.contains("emoji_reaction") == true ||
ti?.pleromaFeatures?.contains("pleroma_emoji_reactions") == true
}
fun canMultipleReaction(ai: SavedAccount, ti: TootInstance?=TootInstance.getCached(ai)) =
when {
ai.isPseudo -> false
ai.isMisskey -> false
else -> ti?.pleromaFeatures?.contains("pleroma_emoji_reactions") == true
}
fun listMyReactions(ai: SavedAccount, ti: TootInstance?) =
@ -67,6 +76,7 @@ object InstanceCapability {
// fedibird extension
ti?.fedibird_capabilities?.contains("emoji_reaction") == true
}
}
class TootInstance(parser: TootParser, src: JsonObject) {
@ -131,6 +141,8 @@ class TootInstance(parser: TootParser, src: JsonObject) {
var misskeyEndpoints: Set<String>? = null
var pleromaFeatures: Set<String>? = null
// XXX: urls をパースしてない。使ってないから…
init {
@ -203,6 +215,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
this.invites_enabled = src.boolean("invites_enabled")
this.fedibird_capabilities = src.jsonArray("fedibird_capabilities")?.stringList()?.toSet()
this.pleromaFeatures = src.jsonObject("pleroma")?.jsonObject("metadata")?.jsonArray("features")?.stringList()?.toSet()
}
}
@ -433,6 +446,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
// no request, no expiration check
fun getCached(apiHost: String) = Host.parse(apiHost).getCacheEntry().cacheData
fun getCached(apiHost: Host) = apiHost.getCacheEntry().cacheData
fun getCached(a:SavedAccount?) = a?.apiHost?.getCacheEntry()?.cacheData
suspend fun get(client: TootApiClient): Pair<TootInstance?, TootApiResult?> = getEx(client)

View File

@ -27,6 +27,7 @@ class TootNotification(parser : TootParser, src : JsonObject) : TimelineItem() {
const val TYPE_FAVOURITE = "favourite"
const val TYPE_REACTION = "reaction" // misskey
const val TYPE_EMOJI_REACTION = "emoji_reaction" // fedibird
const val TYPE_EMOJI_REACTION_PLEROMA = "pleroma:emoji_reaction" // pleroma
const val TYPE_FOLLOW_REQUEST = "follow_request"
const val TYPE_FOLLOW_REQUEST_MISSKEY = "receiveFollowRequest"
@ -100,6 +101,8 @@ class TootNotification(parser : TootParser, src : JsonObject) : TimelineItem() {
reaction = src.jsonObject("emoji_reaction")
?.notEmpty()
?.let{ TootReaction.parseFedibird(it)}
// pleroma unicode emoji
?: src.string("emoji")?.let{ TootReaction(name=it)}
}
}
}

View File

@ -34,8 +34,7 @@ class TootReaction(
// (fedibird絵文字リアクション) userストリームのemoji_reactionイベントで設定される。
val status_id: EntityId? = null,
) {
) {
companion object {
fun appendDomain(name: String, domain: String?) =
@ -99,6 +98,8 @@ class TootReaction(
private fun isUnicodeEmoji(code: String): Boolean =
code.any { it.code >= 0x7f }
fun isCustomEmoji(code: String): Boolean = !isUnicodeEmoji(code)
fun splitEmojiDomain(code: String): Pair<String?, String?> {
// unicode絵文字ならnull,nullを返す
if (isUnicodeEmoji(code)) return Pair(null, null)
@ -110,7 +111,7 @@ class TootReaction(
fun canReaction(
access_info: SavedAccount,
ti: TootInstance? = TootInstance.getCached(access_info.apiHost)
ti: TootInstance? = TootInstance.getCached(access_info)
) = InstanceCapability.emojiReaction(access_info, ti)
fun decodeEmojiQuery(jsonText: String?): List<TootReaction> =
@ -157,6 +158,8 @@ class TootReaction(
return EmojiDecoder.decodeEmoji(options, code)
}
}
fun splitEmojiDomain() =
@ -252,7 +255,13 @@ class TootReaction(
class TootReactionSet(val isMisskey: Boolean) : LinkedList<TootReaction>() {
var myReaction: TootReaction? = null
fun isMyReaction(reaction: TootReaction?): Boolean {
return reaction?.me == true
}
fun hasReaction() = any { it.count > 0 }
fun hasMyReaction() = any { it.count > 0 && isMyReaction(it) }
private fun getRaw(name: String?): TootReaction? =
find { it.name == name }
@ -274,10 +283,7 @@ class TootReactionSet(val isMisskey: Boolean) : LinkedList<TootReaction>() {
add(TootReaction(name = key, count = v))
}
if (myReactionCode != null) {
find { it.name == myReactionCode }?.let {
it.me = true
myReaction = it
}
forEach { it.me = (it.name == myReactionCode) }
}
}.notEmpty()
@ -289,7 +295,6 @@ class TootReactionSet(val isMisskey: Boolean) : LinkedList<TootReaction>() {
val tr = TootReaction.parseFedibird(it)
if (tr.count > 0) add(tr)
}
myReaction = find { it.me }
}.notEmpty()
}
}

View File

@ -604,8 +604,8 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
this.replies_count = src.long("replies_count")
this.reactionSet = TootReactionSet.parseFedibird(
src.jsonArray("emoji_reactions"),
// not used src.boolean("emoji_reactioned")
src.jsonArray("emoji_reactions")
?: src.jsonObject("pleroma")?.jsonArray("emoji_reactions")
)
when (parser.serviceType) {
@ -959,7 +959,6 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
fun updateReactionMastodonByEvent( newReaction: TootReaction ) {
synchronized(this) {
var reactionSet = this.reactionSet
if( newReaction.count <= 0 ){
reactionSet?.get(newReaction.name)?.let{ reactionSet?.remove(it) }
@ -980,7 +979,6 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
else -> old.count = newReaction.count
}
}
reactionSet?.myReaction = reactionSet?.find { it.me }
}
}
@ -994,6 +992,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
code ?: return false
synchronized(this) {
if (emoji != null) {
if (custom_emojis == null) custom_emojis = HashMap()
custom_emojis?.put(emoji.mapKey, emoji)
@ -1006,11 +1005,11 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
}
if (byMe) {
if (reactionSet.myReaction != null) {
// 自分でリアクションしたらUIで更新した後にストリーミングイベントが届くことがある
return false
}
// 自分でリアクションしたらUIで更新した後にストリーミングイベントが届くことがある
// その場合はカウントを変更しない
if(reactionSet.any{ it.me && it.name == code}) return false
}
log.d("increaseReaction noteId=$id byMe=$byMe caller=$caller")
// カウントを増やす
@ -1018,7 +1017,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
reactionSet[code]?.also { it.count = max(0, it.count + 1L) }
?: TootReaction(name = code, count = 1L).also { reactionSet.add(it) }
if(byMe) reactionSet.myReaction = reaction
if(byMe) reaction.me = true
return true
}
@ -1037,14 +1036,17 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
if (byMe) {
// 自分でリアクションしたらUIで更新した後にストリーミングイベントが届くことがある
reactionSet.myReaction ?: return false
reactionSet.myReaction = null
// その場合はカウントを変更しない
if(reactionSet.any{ !it.me && it.name == code}) return false
}
log.d("decreaseReaction noteId=$id byMe=$byMe caller=$caller")
// カウントを減らす
reactionSet[code]?.let { it.count = max(0L, it.count - 1L) }
val reaction = reactionSet[code]
?.also { it.count = max(0L, it.count - 1L) }
if(byMe) reaction?.me = false
return true
}
@ -1289,8 +1291,6 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
delta = abs(delta)
when {
delta < 1000L -> return context.getString(R.string.time_within_second)

View File

@ -880,6 +880,7 @@ class TaskRunner(
TootNotification.TYPE_FAVOURITE ->
context.getString(R.string.display_name_favourited_by, name)
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION ->
context.getString(R.string.display_name_reaction_by, name)

View File

@ -128,6 +128,7 @@ class StreamConnection(
if (StreamManager.traceDelivery) log.v("$name fireTimelineItem")
eachCallbackForAcct { it.onEmojiReactionNotification(item) }
}
private fun fireEmojiReactionEvent(item: TootReaction) {
if (StreamManager.traceDelivery) log.v("$name fireTimelineItem")
eachCallbackForAcct { it.onEmojiReactionEvent(item) }
@ -272,7 +273,11 @@ class StreamConnection(
else -> when (payload) {
is TimelineItem -> {
if (payload is TootNotification && payload.type == TootNotification.TYPE_EMOJI_REACTION) {
if (payload is TootNotification &&
(payload.type == TootNotification.TYPE_EMOJI_REACTION ||
payload.type == TootNotification.TYPE_EMOJI_REACTION_PLEROMA)
) {
log.d("emoji_reaction (notification) ${payload.status?.id}")
fireEmojiReactionNotification(payload)
}

View File

@ -1121,6 +1121,7 @@ class SavedAccount(
TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
TootNotification.TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY -> notification_follow_request
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
TootNotification.TYPE_EMOJI_REACTION,
TootNotification.TYPE_REACTION -> notification_reaction

View File

@ -1092,4 +1092,5 @@
<string name="saved">保存しました</string>
<string name="not_available_for_current_accounts">この機能を利用できるアカウントがありません</string>
<string name="confirm_reaction">%2$s から %1$s でリアクションしますか?</string>
<string name="confirm_reaction_to_pleroma">%2$s からカスタム絵文字 %1$s でリアクションしようとしていますが、送り先 %3$s はカスタム絵文字のリアクションに対応してないかもしれません。続けますか?</string>
</resources>

View File

@ -1105,4 +1105,5 @@
<string name="saved">saved.</string>
<string name="not_available_for_current_accounts">Unavailable for current accounts</string>
<string name="confirm_reaction">Reaction %1$s from %2$s?</string>
<string name="confirm_reaction_to_pleroma">Reaction custom emoji %1$s from %2$s to server %3$s that may not support custom emoji reaction. Continue?</string>
</resources>