リアクション機能の改善
This commit is contained in:
parent
44b1a5b54a
commit
abef450f8f
@ -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 ?: "")
|
||||
|
@ -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 ->
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user