2021-05-23 14:54:52 +02:00
|
|
|
|
package jp.juggler.subwaytooter.action
|
|
|
|
|
|
|
|
|
|
import androidx.appcompat.app.AlertDialog
|
|
|
|
|
import jp.juggler.subwaytooter.*
|
|
|
|
|
import jp.juggler.subwaytooter.api.*
|
2021-05-27 07:54:04 +02:00
|
|
|
|
import jp.juggler.subwaytooter.api.entity.*
|
2021-05-23 14:54:52 +02:00
|
|
|
|
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
|
|
|
|
import jp.juggler.subwaytooter.dialog.EmojiPicker
|
2021-05-27 04:15:59 +02:00
|
|
|
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
2021-05-23 14:54:52 +02:00
|
|
|
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
|
|
|
|
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
|
|
|
|
import jp.juggler.subwaytooter.table.AcctColor
|
|
|
|
|
import jp.juggler.subwaytooter.table.SavedAccount
|
|
|
|
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
|
|
|
|
import jp.juggler.util.*
|
|
|
|
|
|
2021-05-27 07:54:04 +02:00
|
|
|
|
private val rePleromaStatusUrl = """/objects/""".toRegex()
|
|
|
|
|
|
|
|
|
|
private fun String.likePleromaStatusUrl(): Boolean {
|
|
|
|
|
return rePleromaStatusUrl.find(this) != null
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 長押しでない普通のリアクション操作
|
|
|
|
|
fun ActMain.reactionAdd(
|
|
|
|
|
column: Column,
|
|
|
|
|
status: TootStatus,
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// Unicode絵文字、 :name: :name@.: :name@domain: name name@domain 等
|
|
|
|
|
codeArg: String? = null,
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
urlArg: String? = null,
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 確認済みなら真
|
2021-06-20 15:12:25 +02:00
|
|
|
|
isConfirmed: Boolean = false,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
) {
|
|
|
|
|
val activity = this@reactionAdd
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val accessInfo = column.accessInfo
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
|
|
|
|
|
if (hasMyReaction && !canMultipleReaction) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
showToast(false, R.string.already_reactioned)
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 21:57:25 +02:00
|
|
|
|
if (codeArg == null) {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
EmojiPicker(activity, accessInfo, closeOnSelected = true) { result ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
var newUrl: String? = null
|
|
|
|
|
val newCode: String = when (val emoji = result.emoji) {
|
|
|
|
|
is UnicodeEmoji -> emoji.unifiedCode
|
|
|
|
|
is CustomEmoji -> {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
newUrl = emoji.staticUrl
|
|
|
|
|
if (accessInfo.isMisskey) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
":${emoji.shortcode}:"
|
|
|
|
|
} else {
|
|
|
|
|
emoji.shortcode
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
reactionAdd(column, status, newCode, newUrl)
|
|
|
|
|
}.show()
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-27 21:57:25 +02:00
|
|
|
|
var code = codeArg
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-06-20 15:12:25 +02:00
|
|
|
|
if (accessInfo.isMisskey) {
|
2021-05-27 21:57:25 +02:00
|
|
|
|
val (name, domain) = TootReaction.splitEmojiDomain(code)
|
|
|
|
|
if (name == null) {
|
|
|
|
|
// unicode emoji
|
|
|
|
|
} else when (domain) {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
null, "", ".", accessInfo.apDomain.ascii -> {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// normalize to local custom emoji
|
2021-05-27 22:06:18 +02:00
|
|
|
|
code = ":$name:"
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
2021-05-27 21:57:25 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
else -> {
|
|
|
|
|
/*
|
|
|
|
|
#misskey のリアクションAPIはリモートのカスタム絵文字のコードをフォールバック絵文字に変更して、
|
|
|
|
|
何の追加情報もなしに204 no contentを返す。
|
|
|
|
|
よってクライアントはAPI応答からフォールバックが発生したことを認識できず、
|
|
|
|
|
後から投稿をリロードするまで気が付かない。
|
|
|
|
|
この挙動はこの挙動は多くのユーザにとって受け入れられないと判断するので、
|
|
|
|
|
クライアント側で事前にエラー扱いにする方が良い。
|
|
|
|
|
*/
|
|
|
|
|
showToast(true, R.string.cant_reaction_remote_custom_emoji, code)
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 07:54:04 +02:00
|
|
|
|
if (canMultipleReaction && TootReaction.isCustomEmoji(code)) {
|
|
|
|
|
showToast(false, "can't reaction with custom emoji from this account")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
if (!isConfirmed) {
|
|
|
|
|
val options = DecodeOptions(
|
|
|
|
|
activity,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
decodeEmoji = true,
|
|
|
|
|
enlargeEmoji = 1.5f,
|
|
|
|
|
enlargeCustomEmoji = 1.5f
|
|
|
|
|
)
|
|
|
|
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
|
|
|
|
DlgConfirm.open(
|
|
|
|
|
activity,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
2021-05-27 04:15:59 +02:00
|
|
|
|
object : DlgConfirm.Callback {
|
|
|
|
|
override var isConfirmEnabled: Boolean
|
2021-06-20 15:12:25 +02:00
|
|
|
|
get() = accessInfo.confirm_reaction
|
2021-05-27 04:15:59 +02:00
|
|
|
|
set(bv) {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.confirm_reaction = bv
|
|
|
|
|
accessInfo.saveSetting()
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
override fun onOK() {
|
|
|
|
|
reactionAdd(
|
|
|
|
|
column,
|
|
|
|
|
status,
|
|
|
|
|
codeArg = code,
|
|
|
|
|
urlArg = urlArg,
|
|
|
|
|
isConfirmed = true
|
|
|
|
|
)
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
})
|
2021-05-27 04:15:59 +02:00
|
|
|
|
return
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
launchMain {
|
|
|
|
|
var resultStatus: TootStatus? = null
|
2021-06-20 15:12:25 +02:00
|
|
|
|
runApiTask(accessInfo) { client ->
|
2021-05-27 07:54:04 +02:00
|
|
|
|
when {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.isMisskey -> client.request(
|
2021-05-27 07:54:04 +02:00
|
|
|
|
"/api/notes/reactions/create",
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.putMisskeyApiToken().apply {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
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 ->
|
|
|
|
|
// 成功すると新しいステータス
|
2021-06-20 15:12:25 +02:00
|
|
|
|
resultStatus = TootParser(activity, accessInfo).status(result.jsonObject)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> client.request(
|
2021-05-27 04:15:59 +02:00
|
|
|
|
"/api/v1/statuses/${status.id}/emoji_reactions/${code.encodePercent("@")}",
|
|
|
|
|
"".toFormRequestBody().toPut()
|
2021-05-27 07:54:04 +02:00
|
|
|
|
)?.also { result ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 成功すると新しいステータス
|
2021-06-20 15:12:25 +02:00
|
|
|
|
resultStatus = TootParser(activity, accessInfo).status(result.jsonObject)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
}?.let { result ->
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
val error = result.error
|
|
|
|
|
if (error != null) {
|
|
|
|
|
activity.showToast(false, error)
|
|
|
|
|
return@launchMain
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
when (val resCode = result.response?.code) {
|
|
|
|
|
in 200 until 300 -> when (val newStatus = resultStatus) {
|
|
|
|
|
null ->
|
|
|
|
|
if (status.increaseReactionMisskey(code, true, caller = "addReaction")) {
|
|
|
|
|
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
|
|
|
|
|
column.fireShowContent(
|
|
|
|
|
reason = "addReaction complete",
|
|
|
|
|
reset = true
|
|
|
|
|
)
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
else ->
|
2021-06-20 15:12:25 +02:00
|
|
|
|
activity.appState.columnList.forEach { column ->
|
|
|
|
|
if (column.accessInfo.acct == accessInfo.acct) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
column.updateEmojiReactionByApiResponse(newStatus)
|
2021-06-20 15:12:25 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
else -> showToast(false, "HTTP error $resCode")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 長押しでない普通のリアクション操作
|
|
|
|
|
fun ActMain.reactionRemove(
|
|
|
|
|
column: Column,
|
|
|
|
|
status: TootStatus,
|
2021-05-27 07:54:04 +02:00
|
|
|
|
reactionArg: TootReaction? = null,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
confirmed: Boolean = false,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
) {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val activity = this
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val accessInfo = column.accessInfo
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 07:54:04 +02:00
|
|
|
|
// 指定されたリアクションまたは自分がリアクションした最初のもの
|
|
|
|
|
val reaction = reactionArg ?: status.reactionSet?.find { it.count > 0 && it.me }
|
|
|
|
|
if (reaction == null) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
showToast(false, R.string.not_reactioned)
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
if (!confirmed) {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val options = DecodeOptions(
|
|
|
|
|
activity,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo,
|
2021-05-27 07:54:04 +02:00
|
|
|
|
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))
|
2021-05-27 04:15:59 +02:00
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
|
.setPositiveButton(R.string.ok) { _, _ ->
|
2021-05-27 07:54:04 +02:00
|
|
|
|
reactionRemove(column, status, reaction, confirmed = true)
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
.show()
|
|
|
|
|
return
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
launchMain {
|
|
|
|
|
var resultStatus: TootStatus? = null
|
2021-06-20 15:12:25 +02:00
|
|
|
|
runApiTask(accessInfo) { client ->
|
2021-05-27 07:54:04 +02:00
|
|
|
|
when {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.isMisskey -> client.request(
|
2021-05-27 04:15:59 +02:00
|
|
|
|
"/api/notes/reactions/delete",
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.putMisskeyApiToken().apply {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
put("noteId", status.id.toString())
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}.toPostRequestBuilder()
|
|
|
|
|
) // 成功すると204 no content
|
|
|
|
|
|
|
|
|
|
canMultipleReaction -> client.request(
|
|
|
|
|
"/api/v1/pleroma/statuses/${status.id}/reactions/${reaction.name.encodePercent("@")}",
|
|
|
|
|
"".toFormRequestBody().toDelete()
|
|
|
|
|
)?.also { result ->
|
|
|
|
|
// 成功すると新しいステータス
|
2021-06-20 15:12:25 +02:00
|
|
|
|
resultStatus = TootParser(activity, accessInfo).status(result.jsonObject)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> client.request(
|
2021-05-27 04:15:59 +02:00
|
|
|
|
"/api/v1/statuses/${status.id}/emoji_unreaction",
|
|
|
|
|
"".toFormRequestBody().toPost()
|
2021-05-27 07:54:04 +02:00
|
|
|
|
)?.also { result ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 成功すると新しいステータス
|
2021-06-20 15:12:25 +02:00
|
|
|
|
resultStatus = TootParser(activity, accessInfo).status(result.jsonObject)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
}?.let { result ->
|
|
|
|
|
val resCode = result.response?.code
|
|
|
|
|
when {
|
|
|
|
|
result.error != null ->
|
|
|
|
|
activity.showToast(false, result.error)
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
resCode !in 200 until 300 -> showToast(false, "HTTP error $resCode")
|
|
|
|
|
else -> {
|
|
|
|
|
when (val newStatus = resultStatus) {
|
|
|
|
|
null ->
|
2021-05-27 07:54:04 +02:00
|
|
|
|
if (status.decreaseReactionMisskey(reaction.name, true, "removeReaction")) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
|
2021-05-27 07:54:04 +02:00
|
|
|
|
column.fireShowContent(reason = "removeReaction complete", reset = true)
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
else ->
|
2021-06-20 15:12:25 +02:00
|
|
|
|
activity.appState.columnList.forEach { column ->
|
|
|
|
|
if (column.accessInfo.acct == accessInfo.acct) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
column.updateEmojiReactionByApiResponse(newStatus)
|
2021-06-20 15:12:25 +02:00
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// リアクションの別アカ操作で使う
|
|
|
|
|
// 選択済みのアカウントと同期済みのステータスにリアクションを行う
|
|
|
|
|
private fun ActMain.reactionWithoutUi(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo: SavedAccount,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
resolvedStatus: TootStatus,
|
|
|
|
|
reactionCode: String? = null,
|
|
|
|
|
reactionImage: String? = null,
|
|
|
|
|
isConfirmed: Boolean = false,
|
|
|
|
|
callback: () -> Unit,
|
|
|
|
|
) {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val activity = this
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
if (reactionCode == null) {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
EmojiPicker(activity, accessInfo, closeOnSelected = true) { result ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
var newUrl: String? = null
|
|
|
|
|
val newCode = when (val emoji = result.emoji) {
|
|
|
|
|
is UnicodeEmoji -> emoji.unifiedCode
|
|
|
|
|
is CustomEmoji -> {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
newUrl = emoji.staticUrl
|
|
|
|
|
if (accessInfo.isMisskey) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
":${emoji.shortcode}:"
|
2021-05-23 14:54:52 +02:00
|
|
|
|
} else {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
emoji.shortcode
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
reactionWithoutUi(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo = accessInfo,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
resolvedStatus = resolvedStatus,
|
|
|
|
|
reactionCode = newCode,
|
|
|
|
|
reactionImage = newUrl,
|
|
|
|
|
isConfirmed = isConfirmed,
|
|
|
|
|
callback = callback
|
|
|
|
|
)
|
|
|
|
|
}.show()
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
if (!isConfirmed) {
|
|
|
|
|
val options = DecodeOptions(
|
2021-05-27 07:54:04 +02:00
|
|
|
|
activity,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
decodeEmoji = true,
|
|
|
|
|
enlargeEmoji = 1.5f,
|
|
|
|
|
enlargeCustomEmoji = 1.5f
|
|
|
|
|
)
|
|
|
|
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
|
|
|
|
|
2021-05-27 07:54:04 +02:00
|
|
|
|
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,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
AcctColor.getNickname(accessInfo),
|
2021-05-27 07:54:04 +02:00
|
|
|
|
resolvedStatus.account.acct.host?.pretty ?: "(null)"
|
|
|
|
|
),
|
|
|
|
|
) {
|
|
|
|
|
reactionWithoutUi(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo = accessInfo,
|
2021-05-27 07:54:04 +02:00
|
|
|
|
resolvedStatus = resolvedStatus,
|
|
|
|
|
reactionCode = reactionCode,
|
|
|
|
|
reactionImage = reactionImage,
|
|
|
|
|
isConfirmed = true,
|
|
|
|
|
callback = callback
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> DlgConfirm.open(
|
|
|
|
|
activity,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
2021-05-27 07:54:04 +02:00
|
|
|
|
object : DlgConfirm.Callback {
|
|
|
|
|
override var isConfirmEnabled: Boolean
|
2021-06-20 15:12:25 +02:00
|
|
|
|
get() = accessInfo.confirm_reaction
|
2021-05-27 07:54:04 +02:00
|
|
|
|
set(bv) {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.confirm_reaction = bv
|
|
|
|
|
accessInfo.saveSetting()
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onOK() {
|
|
|
|
|
reactionWithoutUi(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo = accessInfo,
|
2021-05-27 07:54:04 +02:00
|
|
|
|
resolvedStatus = resolvedStatus,
|
|
|
|
|
reactionCode = reactionCode,
|
|
|
|
|
reactionImage = reactionImage,
|
|
|
|
|
isConfirmed = true,
|
|
|
|
|
callback = callback
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
return
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
2021-05-27 07:54:04 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
launchMain {
|
|
|
|
|
// var resultStatus: TootStatus? = null
|
2021-06-20 15:12:25 +02:00
|
|
|
|
runApiTask(accessInfo) { client ->
|
2021-05-27 07:54:04 +02:00
|
|
|
|
when {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.isMisskey -> client.request(
|
2021-05-27 04:15:59 +02:00
|
|
|
|
"/api/notes/reactions/create",
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo.putMisskeyApiToken().apply {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
put("noteId", resolvedStatus.id.toString())
|
|
|
|
|
put("reaction", reactionCode)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}.toPostRequestBuilder()
|
|
|
|
|
) // 成功すると204 no content
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 07:54:04 +02:00
|
|
|
|
canMultipleReaction -> client.request(
|
|
|
|
|
"/api/v1/pleroma/statuses/${resolvedStatus.id}/reactions/${reactionCode.encodePercent("@")}",
|
|
|
|
|
"".toFormRequestBody().toPut()
|
|
|
|
|
) // 成功すると更新された投稿
|
|
|
|
|
|
|
|
|
|
else -> client.request(
|
2021-05-27 04:15:59 +02:00
|
|
|
|
"/api/v1/statuses/${resolvedStatus.id}/emoji_reactions/${reactionCode.encodePercent()}",
|
|
|
|
|
"".toFormRequestBody().toPut()
|
2021-05-27 07:54:04 +02:00
|
|
|
|
) // 成功すると更新された投稿
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
}?.let { result ->
|
|
|
|
|
when (val error = result.error) {
|
|
|
|
|
null -> callback()
|
|
|
|
|
else -> showToast(true, error)
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// リアクションの別アカ操作で使う
|
|
|
|
|
// 選択済みのアカウントと同期済みのステータスと同期まえのリアクションから、同期後のリアクションコードを計算する
|
|
|
|
|
// 解決できなかった場合はnullを返す
|
|
|
|
|
private fun ActMain.reactionFixCode(
|
|
|
|
|
timelineAccount: SavedAccount,
|
|
|
|
|
actionAccount: SavedAccount,
|
|
|
|
|
reaction: TootReaction,
|
|
|
|
|
): String? {
|
|
|
|
|
val pair = reaction.splitEmojiDomain()
|
|
|
|
|
pair.first ?: return reaction.name // null または Unicode絵文字
|
|
|
|
|
|
|
|
|
|
val srcDomain = when (val d = pair.second) {
|
|
|
|
|
null, ".", "" -> timelineAccount.apDomain
|
|
|
|
|
else -> Host.parse(d)
|
|
|
|
|
}
|
|
|
|
|
// リアクション者から見てローカルな絵文字
|
|
|
|
|
if (srcDomain == actionAccount.apDomain) {
|
|
|
|
|
return when {
|
|
|
|
|
actionAccount.isMisskey -> ":${pair.first}:"
|
|
|
|
|
else -> pair.first
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
|
|
|
|
// リアクション者からみてリモートの絵文字
|
2021-06-20 15:12:25 +02:00
|
|
|
|
val newName = "${pair.first}@$srcDomain"
|
2021-05-27 04:15:59 +02:00
|
|
|
|
|
|
|
|
|
if (actionAccount.isMisskey) {
|
|
|
|
|
/*
|
|
|
|
|
Misskey のリアクションAPIはリモートのカスタム絵文字のコードをフォールバック絵文字に変更して、
|
|
|
|
|
何の追加情報もなしに204 no contentを返す。
|
|
|
|
|
よってクライアントはAPI応答からフォールバックが発生したことを認識できず、
|
|
|
|
|
後から投稿をリロードするまで気が付かない。
|
|
|
|
|
この挙動はこの挙動は多くのユーザにとって受け入れられないと判断するので、
|
|
|
|
|
クライアント側で事前にエラー扱いにする方が良い。
|
|
|
|
|
*/
|
|
|
|
|
} else {
|
|
|
|
|
// Fedibirdの場合、ステータスを同期した時点で絵文字も同期されてると期待できるのだろうか?
|
|
|
|
|
// 実際に試してみると
|
|
|
|
|
// nightly.fedibird.comの投稿にローカルな絵文字を付けた後、
|
|
|
|
|
// その投稿のURLをfedibird.comの検索欄にいれてローカルに同期すると、
|
|
|
|
|
// すでにインポート済みの投稿だとリアクション集合は古いままなのだった。
|
|
|
|
|
//
|
|
|
|
|
// if (resolvedStatus.reactionSet?.any { it.name == newName } == true)
|
|
|
|
|
|
|
|
|
|
return newName
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// エラー
|
|
|
|
|
showToast(true, R.string.cant_reaction_remote_custom_emoji, newName)
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun ActMain.reactionFromAnotherAccount(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
timelineAccount: SavedAccount,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
statusArg: TootStatus?,
|
|
|
|
|
reaction: TootReaction? = null,
|
|
|
|
|
) {
|
|
|
|
|
statusArg ?: return
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val activity = this
|
2021-05-27 04:15:59 +02:00
|
|
|
|
|
|
|
|
|
fun afterResolveStatus(actionAccount: SavedAccount, resolvedStatus: TootStatus) {
|
|
|
|
|
val code = if (reaction == null) {
|
|
|
|
|
null // あとで選択する
|
|
|
|
|
} else {
|
|
|
|
|
reactionFixCode(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
timelineAccount = timelineAccount,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
actionAccount = actionAccount,
|
|
|
|
|
reaction = reaction,
|
|
|
|
|
) ?: return // エラー終了の場合がある
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
reactionWithoutUi(
|
2021-06-20 15:12:25 +02:00
|
|
|
|
accessInfo = actionAccount,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
resolvedStatus = resolvedStatus,
|
|
|
|
|
reactionCode = code,
|
2021-06-20 15:12:25 +02:00
|
|
|
|
callback = reactionCompleteCallback,
|
2021-05-27 04:15:59 +02:00
|
|
|
|
)
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
launchMain {
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
val list = accountListCanReaction() ?: return@launchMain
|
|
|
|
|
if (list.isEmpty()) {
|
|
|
|
|
showToast(false, R.string.not_available_for_current_accounts)
|
|
|
|
|
return@launchMain
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
pickAccount(
|
|
|
|
|
accountListArg = list.toMutableList(),
|
|
|
|
|
bAuto = false,
|
|
|
|
|
message = activity.getString(R.string.account_picker_reaction)
|
|
|
|
|
)?.let { action_account ->
|
2021-06-20 15:12:25 +02:00
|
|
|
|
if (calcCrossAccountMode(timelineAccount, action_account).isNotRemote) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
afterResolveStatus(action_account, statusArg)
|
|
|
|
|
} else {
|
|
|
|
|
var newStatus: TootStatus? = null
|
2021-05-27 07:54:04 +02:00
|
|
|
|
runApiTask(action_account, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
val (result, status) = client.syncStatus(action_account, statusArg)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
newStatus = status
|
|
|
|
|
result
|
|
|
|
|
}?.let { result ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
result.error?.let {
|
|
|
|
|
activity.showToast(true, it)
|
|
|
|
|
return@launchMain
|
|
|
|
|
}
|
|
|
|
|
newStatus?.let { afterResolveStatus(action_account, it) }
|
2021-05-23 14:54:52 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
}
|
2021-06-22 10:31:51 +02:00
|
|
|
|
|
|
|
|
|
fun ActMain.clickReaction(accessInfo: SavedAccount, column: Column, status: TootStatus) {
|
|
|
|
|
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
|
|
|
|
val hasMyReaction = status.reactionSet?.hasMyReaction() == true
|
|
|
|
|
val bRemoveButton = hasMyReaction && !canMultipleReaction
|
|
|
|
|
when {
|
|
|
|
|
!TootReaction.canReaction(accessInfo) ->
|
|
|
|
|
reactionFromAnotherAccount(
|
|
|
|
|
accessInfo,
|
|
|
|
|
status
|
|
|
|
|
)
|
|
|
|
|
bRemoveButton ->
|
|
|
|
|
reactionRemove(column, status)
|
|
|
|
|
else ->
|
|
|
|
|
reactionAdd(column, status)
|
|
|
|
|
}
|
|
|
|
|
}
|