2021-05-23 14:54:52 +02:00
|
|
|
|
package jp.juggler.subwaytooter.action
|
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
import jp.juggler.subwaytooter.ActMain
|
|
|
|
|
import jp.juggler.subwaytooter.R
|
|
|
|
|
import jp.juggler.subwaytooter.api.ApiTask
|
|
|
|
|
import jp.juggler.subwaytooter.api.TootParser
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.Host
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.InstanceCapability
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.TootReaction
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
|
|
|
|
import jp.juggler.subwaytooter.api.runApiTask
|
|
|
|
|
import jp.juggler.subwaytooter.api.syncStatus
|
2021-06-28 09:09:00 +02:00
|
|
|
|
import jp.juggler.subwaytooter.column.Column
|
|
|
|
|
import jp.juggler.subwaytooter.column.fireShowContent
|
|
|
|
|
import jp.juggler.subwaytooter.column.updateEmojiReactionByApiResponse
|
2022-05-29 15:38:21 +02:00
|
|
|
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
|
|
|
|
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
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
|
2023-02-22 16:04:00 +01:00
|
|
|
|
import jp.juggler.subwaytooter.util.emojiSizeMode
|
2021-05-23 14:54:52 +02:00
|
|
|
|
import jp.juggler.subwaytooter.table.SavedAccount
|
2023-02-04 21:52:26 +01:00
|
|
|
|
import jp.juggler.subwaytooter.table.accountListCanReaction
|
|
|
|
|
import jp.juggler.subwaytooter.table.daoAcctColor
|
|
|
|
|
import jp.juggler.subwaytooter.table.daoSavedAccount
|
2021-05-23 14:54:52 +02:00
|
|
|
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
2023-01-13 13:22:25 +01:00
|
|
|
|
import jp.juggler.util.coroutine.launchAndShowError
|
|
|
|
|
import jp.juggler.util.coroutine.launchMain
|
|
|
|
|
import jp.juggler.util.data.encodePercent
|
|
|
|
|
import jp.juggler.util.log.showToast
|
|
|
|
|
import jp.juggler.util.network.*
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
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) {
|
2022-05-29 15:38:21 +02:00
|
|
|
|
launchEmojiPicker(
|
|
|
|
|
activity,
|
|
|
|
|
accessInfo,
|
|
|
|
|
closeOnSelected = true
|
|
|
|
|
) { emoji, _ ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
var newUrl: String? = null
|
2022-05-29 15:38:21 +02:00
|
|
|
|
val newCode: String = when (emoji) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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)
|
2022-05-29 15:38:21 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
activity.launchAndShowError {
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
if (!isConfirmed) {
|
|
|
|
|
val options = DecodeOptions(
|
|
|
|
|
activity,
|
|
|
|
|
accessInfo,
|
|
|
|
|
decodeEmoji = true,
|
2023-02-26 00:15:57 +01:00
|
|
|
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
|
|
|
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
2023-02-21 16:17:29 +01:00
|
|
|
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
2022-05-29 15:38:21 +02:00
|
|
|
|
)
|
|
|
|
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
|
|
|
|
confirm(
|
2023-02-04 21:52:26 +01:00
|
|
|
|
getString(
|
|
|
|
|
R.string.confirm_reaction,
|
|
|
|
|
emojiSpan,
|
|
|
|
|
daoAcctColor.getNickname(accessInfo)
|
|
|
|
|
),
|
2023-02-06 03:10:24 +01:00
|
|
|
|
accessInfo.confirmReaction,
|
2022-05-29 15:38:21 +02:00
|
|
|
|
) { newConfirmEnabled ->
|
2023-02-06 03:10:24 +01:00
|
|
|
|
accessInfo.confirmReaction = newConfirmEnabled
|
|
|
|
|
daoSavedAccount.save(accessInfo)
|
2022-05-29 15:38:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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 ->
|
2022-05-29 15:38:21 +02:00
|
|
|
|
result.error?.let { error(it) }
|
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
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
launchAndShowError {
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
if (!confirmed) {
|
|
|
|
|
val options = DecodeOptions(
|
|
|
|
|
activity,
|
|
|
|
|
accessInfo,
|
|
|
|
|
decodeEmoji = true,
|
2023-02-26 00:15:57 +01:00
|
|
|
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
|
|
|
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
2023-02-21 16:17:29 +01:00
|
|
|
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
2022-05-29 15:38:21 +02:00
|
|
|
|
)
|
|
|
|
|
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
|
|
|
|
confirm(R.string.reaction_remove_confirm, emojiSpan)
|
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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 ->
|
2023-01-13 13:22:25 +01:00
|
|
|
|
if (status.decreaseReactionMisskey(
|
|
|
|
|
reaction.name,
|
2022-05-29 15:38:21 +02:00
|
|
|
|
true,
|
2023-01-13 13:22:25 +01:00
|
|
|
|
"removeReaction"
|
|
|
|
|
)
|
2022-05-29 15:38:21 +02:00
|
|
|
|
) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
|
2023-01-13 13:22:25 +01: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,
|
|
|
|
|
callback: () -> Unit,
|
|
|
|
|
) {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
val activity = this
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
if (reactionCode == null) {
|
2022-05-29 15:38:21 +02:00
|
|
|
|
launchEmojiPicker(activity, accessInfo, closeOnSelected = true) { emoji, _ ->
|
2021-05-27 04:15:59 +02:00
|
|
|
|
var newUrl: String? = null
|
2022-05-29 15:38:21 +02:00
|
|
|
|
val newCode = when (emoji) {
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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,
|
|
|
|
|
callback = callback
|
|
|
|
|
)
|
2022-05-29 15:38:21 +02:00
|
|
|
|
}
|
2021-05-27 04:15:59 +02:00
|
|
|
|
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
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
val options = DecodeOptions(
|
|
|
|
|
activity,
|
|
|
|
|
accessInfo,
|
|
|
|
|
decodeEmoji = true,
|
2023-02-26 00:15:57 +01:00
|
|
|
|
enlargeEmoji = DecodeOptions.emojiScaleReaction,
|
|
|
|
|
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
|
2023-02-21 16:17:29 +01:00
|
|
|
|
emojiSizeMode = accessInfo.emojiSizeMode(),
|
2022-05-29 15:38:21 +02:00
|
|
|
|
)
|
|
|
|
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
|
|
|
|
val isCustomEmoji = TootReaction.isCustomEmoji(reactionCode)
|
|
|
|
|
val url = resolvedStatus.url
|
|
|
|
|
|
|
|
|
|
launchAndShowError {
|
2021-05-27 07:54:04 +02:00
|
|
|
|
when {
|
2022-05-29 15:38:21 +02:00
|
|
|
|
isCustomEmoji && canMultipleReaction ->
|
|
|
|
|
error("can't reaction with custom emoji from this account")
|
|
|
|
|
|
|
|
|
|
isCustomEmoji && url?.likePleromaStatusUrl() == true -> confirm(
|
|
|
|
|
R.string.confirm_reaction_to_pleroma,
|
|
|
|
|
emojiSpan,
|
2023-02-04 21:52:26 +01:00
|
|
|
|
daoAcctColor.getNickname(accessInfo),
|
2022-05-29 15:38:21 +02:00
|
|
|
|
resolvedStatus.account.acct.host?.pretty ?: "(null)"
|
|
|
|
|
)
|
2021-05-27 07:54:04 +02:00
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
else -> confirm(
|
2023-02-04 21:52:26 +01:00
|
|
|
|
getString(
|
|
|
|
|
R.string.confirm_reaction,
|
|
|
|
|
emojiSpan,
|
|
|
|
|
daoAcctColor.getNickname(accessInfo)
|
|
|
|
|
),
|
2023-02-06 03:10:24 +01:00
|
|
|
|
accessInfo.confirmReaction,
|
2022-05-29 15:38:21 +02:00
|
|
|
|
) { newConfirmEnabled ->
|
2023-02-06 03:10:24 +01:00
|
|
|
|
accessInfo.confirmReaction = newConfirmEnabled
|
|
|
|
|
daoSavedAccount.save(accessInfo)
|
2022-05-29 15:38:21 +02:00
|
|
|
|
}
|
2021-05-27 07:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-27 04:15:59 +02:00
|
|
|
|
// 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(
|
2022-05-29 15:38:21 +02:00
|
|
|
|
"/api/v1/pleroma/statuses/${resolvedStatus.id}/reactions/${
|
|
|
|
|
reactionCode.encodePercent("@")
|
|
|
|
|
}",
|
2021-05-27 07:54:04 +02:00
|
|
|
|
"".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
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
launchMain {
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
fun afterResolveStatus(
|
|
|
|
|
actionAccount: SavedAccount,
|
|
|
|
|
resolvedStatus: TootStatus,
|
|
|
|
|
) {
|
|
|
|
|
val code = if (reaction == null) {
|
|
|
|
|
null // あとで選択する
|
|
|
|
|
} else {
|
|
|
|
|
reactionFixCode(
|
|
|
|
|
timelineAccount = timelineAccount,
|
|
|
|
|
actionAccount = actionAccount,
|
|
|
|
|
reaction = reaction,
|
|
|
|
|
) ?: return // エラー終了の場合がある
|
|
|
|
|
}
|
2021-05-23 14:54:52 +02:00
|
|
|
|
|
2022-05-29 15:38:21 +02:00
|
|
|
|
reactionWithoutUi(
|
|
|
|
|
accessInfo = actionAccount,
|
|
|
|
|
resolvedStatus = resolvedStatus,
|
|
|
|
|
reactionCode = code,
|
|
|
|
|
callback = reactionCompleteCallback,
|
|
|
|
|
)
|
|
|
|
|
}
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|