めいすきーのリアクションの@を含む絵文字の表示

This commit is contained in:
tateisu 2021-01-03 20:49:21 +09:00
parent 2b8f0a63cc
commit 1902b58fc0
4 changed files with 102 additions and 80 deletions

View File

@ -1191,7 +1191,7 @@ internal class ItemViewHolder(
enlargeEmoji = 1.5f, enlargeEmoji = 1.5f,
enlargeCustomEmoji = 1.5f enlargeCustomEmoji = 1.5f
) )
val ssb = MisskeyReaction.toSpannableStringBuilder(misskeyReaction,options) val ssb = MisskeyReaction.toSpannableStringBuilder(misskeyReaction,options,boost_status)
ssb.append(" ") ssb.append(" ")
ssb.append(who.decodeDisplayName(activity) ssb.append(who.decodeDisplayName(activity)
.intoStringResource(activity, string_id)) .intoStringResource(activity, string_id))
@ -2546,7 +2546,7 @@ internal class ItemViewHolder(
val key = entry.key val key = entry.key
val count = entry.value val count = entry.value
if(count <= 0) continue if(count <= 0) continue
val ssb = MisskeyReaction.toSpannableStringBuilder(key,options) val ssb = MisskeyReaction.toSpannableStringBuilder(key,options,status)
.also{ it.append(" $count")} .also{ it.append(" $count")}
val b = Button(activity).apply { val b = Button(activity).apply {
@ -2599,7 +2599,7 @@ internal class ItemViewHolder(
llExtra.addView(box) llExtra.addView(box)
} }
private fun addReaction(status : TootStatus, codeArg : String?) { private fun addReaction(status : TootStatus, code : String?) {
if(status.myReaction?.isNotEmpty() == true) { if(status.myReaction?.isNotEmpty() == true) {
activity.showToast(false, R.string.already_reactioned) activity.showToast(false, R.string.already_reactioned)
@ -2608,7 +2608,7 @@ internal class ItemViewHolder(
if(access_info.isPseudo || ! access_info.isMisskey) return if(access_info.isPseudo || ! access_info.isMisskey) return
if(codeArg == null) { if(code == null) {
EmojiPicker(activity, access_info,closeOnSelected = true) { name, instance, _, _, _ -> EmojiPicker(activity, access_info,closeOnSelected = true) { name, instance, _, _, _ ->
val item = EmojiMap.sShortNameToEmojiInfo[name] val item = EmojiMap.sShortNameToEmojiInfo[name]
val code = if(item == null || instance != null) { val code = if(item == null || instance != null) {
@ -2636,12 +2636,8 @@ internal class ItemViewHolder(
TootTaskRunner(activity, progress_style = TootTaskRunner.PROGRESS_NONE).run(access_info, TootTaskRunner(activity, progress_style = TootTaskRunner.PROGRESS_NONE).run(access_info,
object : TootTask { object : TootTask {
var code :String = codeArg
override suspend fun background(client : TootApiClient) : TootApiResult? { override suspend fun background(client : TootApiClient) : TootApiResult? {
code = MisskeyReaction.toLegacyReaction(client,code)
val params = access_info.putMisskeyApiToken().apply { val params = access_info.putMisskeyApiToken().apply {
put("noteId", status.id.toString()) put("noteId", status.id.toString())
put("reaction", code) put("reaction", code)

View File

@ -1309,7 +1309,7 @@ object Action_Toot {
nCrossAccountMode: Int, nCrossAccountMode: Int,
callback: () -> Unit, callback: () -> Unit,
bSet: Boolean = true, bSet: Boolean = true,
codeArg: String? = null code: String? = null
) { ) {
if (access_info.isPseudo || !access_info.isMisskey) return if (access_info.isPseudo || !access_info.isMisskey) return
@ -1319,34 +1319,31 @@ object Action_Toot {
return return
} }
if (codeArg == null) { if (code == null) {
if (bSet) if (!bSet) error("will not happen")
EmojiPicker(activity, access_info, closeOnSelected = true) { name, instance, _, _, _ -> EmojiPicker(activity, access_info, closeOnSelected = true) { name, instance, _, _, _ ->
val item = EmojiMap.sShortNameToEmojiInfo[name] val item = EmojiMap.sShortNameToEmojiInfo[name]
val newCode = if (item == null || instance != null) { val newCode = if (item == null || instance != null) {
":$name:" ":$name:"
} else { } else {
item.unified item.unified
} }
reaction( reaction(
activity, activity,
access_info, access_info,
arg_status, arg_status,
status_owner_acct, status_owner_acct,
nCrossAccountMode, nCrossAccountMode,
callback, callback,
bSet, bSet,
newCode newCode
) )
}.show() }.show()
return return
} }
TootTaskRunner(activity, TootTaskRunner.PROGRESS_NONE).run(access_info, object : TootTask { TootTaskRunner(activity, TootTaskRunner.PROGRESS_NONE).run(access_info, object : TootTask {
var code: String = codeArg
override suspend fun background(client: TootApiClient): TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
val target_status = if (nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE) { val target_status = if (nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE) {
@ -1378,8 +1375,6 @@ object Action_Toot {
// 成功すると204 no content // 成功すると204 no content
} else { } else {
code = MisskeyReaction.toLegacyReaction(client, code)
client.request( client.request(
"/api/notes/reactions/create", "/api/notes/reactions/create",
access_info.putMisskeyApiToken().apply { access_info.putMisskeyApiToken().apply {
@ -1433,7 +1428,7 @@ object Action_Toot {
status_owner, status_owner,
calcCrossAccountMode(timeline_account, action_account), calcCrossAccountMode(timeline_account, action_account),
activity.reaction_complete_callback, activity.reaction_complete_callback,
codeArg = code code = code
) )
} }
} }

View File

@ -1,15 +1,9 @@
package jp.juggler.subwaytooter.api.entity package jp.juggler.subwaytooter.api.entity
import android.graphics.drawable.PictureDrawable
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.widget.ImageView
import com.bumptech.glide.Glide
import jp.juggler.emoji.EmojiMap
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.App1 import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.Pref import jp.juggler.subwaytooter.Pref
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.span.NetworkEmojiSpan import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.DecodeOptions import jp.juggler.subwaytooter.util.DecodeOptions
@ -46,51 +40,58 @@ object MisskeyReaction {
"star" to "\u2B50", // リモートからのFavを示す代替リアクション。ピッカーには表示しない "star" to "\u2B50", // リモートからのFavを示す代替リアクション。ピッカーには表示しない
) )
fun toSpannableStringBuilder(code: String, options: DecodeOptions): SpannableStringBuilder { private val reCustomEmoji = """\A:([^:]+):\z""".toRegex()
fun toSpannableStringBuilder(
code: String,
options: DecodeOptions,
status:TootStatus?
): SpannableStringBuilder {
// 古い形式の絵文字はUnicode絵文字にする // 古い形式の絵文字はUnicode絵文字にする
oldReactions[code]?.let { oldReactions[code]?.let {
return EmojiDecoder.decodeEmoji(options, it) return EmojiDecoder.decodeEmoji(options, it)
} }
// カスタム絵文字 fun CustomEmoji.toSpannableStringBuilder():SpannableStringBuilder?{
val customCode = code.replace(":", "") return if (Pref.bpDisableEmojiAnimation(App1.pref)) {
if (customCode != code) { static_url
val accessInfo = options.linkHelper as? SavedAccount } else {
if (accessInfo != null) { url
val emojiUrl = }?.let{
App1.custom_emoji_lister SpannableStringBuilder(code).apply {
.getMap(accessInfo) setSpan(
?.get(customCode) NetworkEmojiSpan(it, scale = options.enlargeCustomEmoji),
?.let { 0,
if (Pref.bpDisableEmojiAnimation(App1.pref)) { length,
it.static_url Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
} else { )
it.url }
}
}
if (emojiUrl != null)
return SpannableStringBuilder(code).apply {
setSpan(
NetworkEmojiSpan(emojiUrl, scale = options.enlargeCustomEmoji),
0,
length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
} }
} }
// カスタム絵文字
val customCode = reCustomEmoji.find(code)?.groupValues?.elementAtOrNull(1)
if(customCode != null){
var ce = status?.custom_emojis?.get( customCode)
if(ce != null) return ce.toSpannableStringBuilder()?: EmojiDecoder.decodeEmoji(options, code)
val accessInfo = options.linkHelper as? SavedAccount
val cols = customCode.split("@",limit = 2)
val key = cols.elementAtOrNull(0)
val domain = cols.elementAtOrNull(1)
if( domain == null || domain=="" || domain=="." || domain == accessInfo?.apiHost?.ascii ){
if( accessInfo != null){
ce = App1.custom_emoji_lister
.getMap(accessInfo)
?.get(key)
if(ce != null) return ce.toSpannableStringBuilder()?: EmojiDecoder.decodeEmoji(options, code)
}
}
}
// unicode絵文字、もしくは :xxx: などのshortcode表現 // unicode絵文字、もしくは :xxx: などのshortcode表現
return EmojiDecoder.decodeEmoji(options, code) return EmojiDecoder.decodeEmoji(options, code)
} }
// Misskey v12 未満はレガシーなリアクションを送ることになる
suspend fun toLegacyReaction(client: TootApiClient, code: String): String {
val(ti,ri) = TootInstance.get(client)
if(ti!=null && ! ti.versionGE(TootInstance.MISSKEY_VERSION_12)){
val entry = oldReactions.entries.firstOrNull { it.value == code }
if( entry != null && entry.key != "star") return entry.key
}
return code
}
} }

View File

@ -20,6 +20,7 @@ import java.util.regex.Pattern
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashMap import kotlin.collections.LinkedHashMap
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min import kotlin.math.min
class FilterTrees( class FilterTrees(
@ -927,6 +928,18 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
return list return list
} }
private fun getAnotherReactionExpression(reaction:String):String{
// :reaction: => reaction
// :reaction@xxx: => reaction@xxx
val customCode = reaction.replace(":","")
// reaction => :reaction@.:
return if( customCode != reaction && !customCode.contains("@"))
":${customCode}@.:"
else
reaction
}
// return true if updated // return true if updated
fun increaseReaction(reaction: String?, byMe: Boolean, caller: String): Boolean { fun increaseReaction(reaction: String?, byMe: Boolean, caller: String): Boolean {
reaction ?: return false reaction ?: return false
@ -948,8 +961,17 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
map = LinkedHashMap() map = LinkedHashMap()
this.reactionCounts = map this.reactionCounts = map
} }
map[reaction] = (map[reaction] ?: 0) + 1
val anotherExpression = getAnotherReactionExpression(reaction)
for( entry in map){
if( entry.key == reaction || entry.key == anotherExpression){
map[entry.key] = entry.value +1
return true
}
}
map[reaction] = 1
return true return true
} }
} }
@ -975,8 +997,16 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
map = LinkedHashMap() map = LinkedHashMap()
this.reactionCounts = map this.reactionCounts = map
} }
map[reaction] = (map[reaction] ?: 1) - 1
val anotherExpression = getAnotherReactionExpression(reaction)
for( entry in map){
if( entry.key == reaction || entry.key == anotherExpression){
map[entry.key] = max(0,entry.value -1)
return true
}
}
map[reaction] = 0
return true return true
} }
} }