MisskeyのMentionでホスト名が省略されていた場合は閲覧者ではなく投稿者のドメインを補う
This commit is contained in:
parent
e182127494
commit
f57ac32c78
|
@ -23,8 +23,11 @@ import androidx.core.app.ActivityCompat
|
|||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.subwaytooter.Styler.defaultColorIcon
|
||||
import jp.juggler.subwaytooter.action.accountRemove
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.runApiTask
|
||||
import jp.juggler.subwaytooter.databinding.ActAccountSettingBinding
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.notification.NotificationHelper
|
||||
|
@ -34,7 +37,10 @@ import jp.juggler.subwaytooter.pref.PrefB
|
|||
import jp.juggler.subwaytooter.pref.PrefS
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.EmojiDecoder
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.openBrowser
|
||||
import jp.juggler.util.*
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
@ -45,9 +51,11 @@ import okhttp3.RequestBody
|
|||
import okio.BufferedSink
|
||||
import org.jetbrains.anko.backgroundColor
|
||||
import org.jetbrains.anko.textColor
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.max
|
||||
|
||||
class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
|
||||
|
@ -942,7 +950,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener,
|
|||
linkHelper = account,
|
||||
emojiMapProfile = src.profile_emojis,
|
||||
emojiMapCustom = src.custom_emojis,
|
||||
mentionDefaultHostDomain = account
|
||||
authorDomain = account,
|
||||
)
|
||||
|
||||
val displayName = src.display_name
|
||||
|
|
|
@ -358,7 +358,7 @@ fun ActPost.initializeFromRedraftStatus(account: SavedAccount, jsonText: String)
|
|||
this,
|
||||
mentionFullAcct = true,
|
||||
mentions = baseStatus.mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
linkHelper = account,
|
||||
)
|
||||
|
||||
var text: CharSequence = if (account.isMisskey) {
|
||||
|
@ -459,7 +459,7 @@ fun ActPost.initializeFromEditStatus(account: SavedAccount, jsonText: String) {
|
|||
this,
|
||||
mentionFullAcct = true,
|
||||
mentions = baseStatus.mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
linkHelper = account,
|
||||
)
|
||||
|
||||
var text: CharSequence = if (account.isMisskey) {
|
||||
|
|
|
@ -24,7 +24,7 @@ fun ActPost.appendContentText(
|
|||
val svEmoji = DecodeOptions(
|
||||
context = this,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = account ?: unknownHostAndDomain
|
||||
authorDomain = account ?: unknownHostAndDomain,
|
||||
).decodeEmoji(src)
|
||||
if (svEmoji.isEmpty()) return
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ fun ActPost.showReplyTo() {
|
|||
linkHelper = account,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = account ?: unknownHostAndDomain
|
||||
).decodeHTML(states.inReplyToText)
|
||||
views.ivReply.setImageUrl(Styler.calcIconRound(views.ivReply.layoutParams),
|
||||
states.inReplyToImage)
|
||||
|
|
|
@ -441,7 +441,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
context,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
mentionDefaultHostDomain = this
|
||||
authorDomain = this
|
||||
).decodeEmoji(sv)
|
||||
}
|
||||
|
||||
|
@ -539,7 +539,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
|||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
mentionDefaultHostDomain = this,
|
||||
authorDomain = this,
|
||||
).decodeHTML(note)
|
||||
.replaceAllEx(reNoteLineFeed, " ")
|
||||
.trimEx()
|
||||
|
|
|
@ -28,7 +28,7 @@ class TootAccountRef(parser: TootParser, account: TootAccount) : TimelineItem()
|
|||
emojiMapProfile = account.profile_emojis,
|
||||
emojiMapCustom = account.custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
mentionDefaultHostDomain = account,
|
||||
authorDomain = account,
|
||||
).decodeHTML(account.note)
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ class TootAnnouncement(parser: TootParser, src: JsonObject) {
|
|||
// attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = parser.linkHelper
|
||||
)
|
||||
|
||||
this.content = src.string("content") ?: ""
|
||||
|
|
|
@ -56,7 +56,7 @@ class TootCard(
|
|||
DecodeOptions(
|
||||
context = parser.context,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = src.account
|
||||
authorDomain = src.account,
|
||||
).decodeHTML(src.content ?: "").toString()
|
||||
},
|
||||
image = src.media_attachments
|
||||
|
|
|
@ -110,7 +110,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
).decodeHTML(this.question ?: "?")
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
this.items = parseChoiceListMastodon(
|
||||
|
@ -193,7 +193,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
this.items = parseChoiceListFriendsNico(
|
||||
|
@ -222,7 +222,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account,
|
||||
authorDomain = status.account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
|
@ -315,7 +315,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
)
|
||||
for (o in objectArray) {
|
||||
val text = reWhitespace
|
||||
|
@ -349,7 +349,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
)
|
||||
for (o in objectArray) {
|
||||
val text = reWhitespace
|
||||
|
@ -383,7 +383,7 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
)
|
||||
for (i in 0 until size) {
|
||||
val text = reWhitespace
|
||||
|
|
|
@ -315,7 +315,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
|
||||
mentionDefaultHostDomain = account
|
||||
authorDomain = account
|
||||
)
|
||||
|
||||
this.decoded_content = options.decodeHTML(content)
|
||||
|
@ -348,7 +348,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
|
||||
mentionDefaultHostDomain = account
|
||||
authorDomain = account
|
||||
)
|
||||
this.decoded_spoiler_text = options.decodeHTML(spoiler_text)
|
||||
|
||||
|
@ -522,7 +522,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = account,
|
||||
authorDomain = account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
)
|
||||
|
||||
|
@ -545,7 +545,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = account,
|
||||
authorDomain = account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
)
|
||||
|
||||
|
@ -750,7 +750,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
authorDomain = account
|
||||
)
|
||||
|
||||
this.decoded_content = options.decodeHTML(content)
|
||||
|
@ -772,7 +772,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = account
|
||||
authorDomain = account
|
||||
)
|
||||
|
||||
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)
|
||||
|
|
|
@ -329,7 +329,7 @@ private fun ColumnViewHolder.showReactions(
|
|||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
mentionDefaultHostDomain = column.accessInfo
|
||||
authorDomain = column.accessInfo
|
||||
)
|
||||
|
||||
val actMain = activity
|
||||
|
|
|
@ -141,7 +141,7 @@ internal class ViewHolderHeaderInstance(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
mentionDefaultHostDomain = accessInfo
|
||||
authorDomain = accessInfo
|
||||
)
|
||||
|
||||
tvShortDescription.text = options
|
||||
|
|
|
@ -664,7 +664,7 @@ internal class ViewHolderHeaderProfile(
|
|||
short = true,
|
||||
emojiMapCustom = who.custom_emojis,
|
||||
emojiMapProfile = who.profile_emojis,
|
||||
mentionDefaultHostDomain = who
|
||||
authorDomain = who
|
||||
)
|
||||
|
||||
val nameTypeface = ActMain.timeline_font_bold
|
||||
|
|
|
@ -34,7 +34,7 @@ internal class ViewHolderHeaderSearch(
|
|||
tvSearchDesc.textColor = column.getContentColor()
|
||||
tvSearchDesc.text = DecodeOptions(
|
||||
activity, accessInfo, decodeEmoji = true,
|
||||
mentionDefaultHostDomain = accessInfo
|
||||
authorDomain = accessInfo
|
||||
)
|
||||
.decodeHTML(column.getHeaderDesc())
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ fun ItemViewHolder.showPreviewCard(status: TootStatus) {
|
|||
val text = DecodeOptions(
|
||||
activity, accessInfo,
|
||||
forceHtml = true,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account
|
||||
).decodeHTML(sb.toString())
|
||||
|
||||
if (text.isNotEmpty()) {
|
||||
|
|
|
@ -157,8 +157,8 @@ class SpanOutputEnv(
|
|||
)
|
||||
),
|
||||
url = url,
|
||||
options.authorDomain,
|
||||
options.linkHelper,
|
||||
options.mentionDefaultHostDomain,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -196,20 +196,23 @@ class SpanOutputEnv(
|
|||
// ユーザが記述したacct
|
||||
val rawAcct = Acct.parse(username, strHost)
|
||||
|
||||
val linkHelper = linkHelper
|
||||
if (linkHelper == null) {
|
||||
// 長いacctを生成する
|
||||
val fullAcct = getFullAcctOrNull(
|
||||
rawAcct,
|
||||
null,
|
||||
options.authorDomain,
|
||||
linkHelper
|
||||
)
|
||||
|
||||
if( fullAcct==null){
|
||||
appendText("@${rawAcct.pretty}")
|
||||
return
|
||||
}
|
||||
|
||||
// 長いacct
|
||||
// MFMでは投稿者のドメインを補うのはサーバ側の仕事の筈なので、options.mentionDefault…は見ない
|
||||
val fullAcct = rawAcct.followHost(linkHelper.apDomain)
|
||||
|
||||
// mentionsメタデータに含まれるacct
|
||||
// ユーザの記述に因らず、サーバのホスト名同じなら短い、そうでなければ長いメンション
|
||||
val shortAcct = when {
|
||||
linkHelper.matchHost(fullAcct.host) -> Acct.parse(username)
|
||||
val shortAcct = when (fullAcct.host) {
|
||||
linkHelper?.apDomain, linkHelper?.apiHost -> Acct.parse(username)
|
||||
else -> fullAcct
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,14 @@ import android.content.Context
|
|||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ReplacementSpan
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.entity.HostAndDomain
|
||||
import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji
|
||||
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
|
||||
import jp.juggler.subwaytooter.api.entity.TootMention
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.util.WordTrieTree
|
||||
import org.jetbrains.anko.collections.forEachReversedByIndex
|
||||
import java.util.*
|
||||
|
||||
class DecodeOptions(
|
||||
val context: Context? = null,
|
||||
|
@ -24,11 +26,13 @@ class DecodeOptions(
|
|||
var unwrapEmojiImageTag: Boolean = false,
|
||||
var enlargeCustomEmoji: Float = 1f,
|
||||
var enlargeEmoji: Float = 1f,
|
||||
var forceHtml: Boolean = false, // force use HTML instead of Misskey Markdown
|
||||
// force use HTML instead of Misskey Markdown
|
||||
var forceHtml: Boolean = false,
|
||||
var mentionFullAcct: Boolean = false,
|
||||
var mentions: ArrayList<TootMention>? = null,
|
||||
// Account.note などmentionsがない状況でメンションリンクをfull acct化するにはアカウント等からapDomainを補う必要がある
|
||||
var mentionDefaultHostDomain: HostAndDomain = linkHelper ?: unknownHostAndDomain,
|
||||
// MFMはメンションのホスト名を補うのに閲覧者ではなく投稿作者のホスト名を必要とする
|
||||
var authorDomain: HostAndDomain? = null,
|
||||
) {
|
||||
|
||||
internal fun isMediaAttachment(url: String?): Boolean =
|
||||
|
|
|
@ -1087,8 +1087,8 @@ object HTMLDecoder {
|
|||
getFullAcctOrNull(
|
||||
mention.acct,
|
||||
href,
|
||||
options.authorDomain,
|
||||
options.linkHelper,
|
||||
options.mentionDefaultHostDomain
|
||||
)?.let { afterFullAcctResolved(it) }
|
||||
} else {
|
||||
|
||||
|
@ -1110,8 +1110,8 @@ object HTMLDecoder {
|
|||
getFullAcctOrNull(
|
||||
rawAcct,
|
||||
href,
|
||||
options.authorDomain,
|
||||
options.linkHelper,
|
||||
options.mentionDefaultHostDomain
|
||||
)?.let { fullAcct ->
|
||||
|
||||
// mentionメタデータを捏造する
|
||||
|
|
|
@ -60,36 +60,36 @@ fun LinkHelper.matchHost(src: String?) = apiHost.match(src) || apDomain.match(sr
|
|||
fun LinkHelper.matchHost(src: Host?) = apiHost == src || apDomain == src
|
||||
fun LinkHelper.matchHost(src: LinkHelper) =
|
||||
apiHost == src.apiHost || apDomain == src.apDomain ||
|
||||
apDomain == src.apiHost || apiHost == src.apDomain
|
||||
apDomain == src.apiHost || apiHost == src.apDomain
|
||||
|
||||
fun LinkHelper.matchHost(src: TootAccount) =
|
||||
apiHost == src.apiHost || apDomain == src.apDomain ||
|
||||
apDomain == src.apiHost || apiHost == src.apDomain
|
||||
apDomain == src.apiHost || apiHost == src.apDomain
|
||||
|
||||
// user や user@host から user@host を返す
|
||||
fun getFullAcctOrNull(
|
||||
rawAcct: Acct,
|
||||
url: String,
|
||||
url: String?,
|
||||
hostDomain1: HostAndDomain? = null,
|
||||
hostDomain2: HostAndDomain? = null
|
||||
) =
|
||||
if (rawAcct.isValidFull) {
|
||||
// 最初から有効なfull acctがあればそれを使う
|
||||
rawAcct
|
||||
} else {
|
||||
// URL中のホスト名を使うが、引数でホストとドメインの対応が提供されていればドメインへの変換を試みる
|
||||
val host = TootAccount.reHostInUrl.matcher(url)
|
||||
.findOrNull()?.groupEx(1)?.let { Host.parse(it) }
|
||||
if (host == null) {
|
||||
null
|
||||
} else {
|
||||
Acct.parse(
|
||||
rawAcct.username,
|
||||
when (host) {
|
||||
hostDomain1?.apiHost -> hostDomain1.apDomain
|
||||
hostDomain2?.apiHost -> hostDomain2.apDomain
|
||||
else -> host
|
||||
}
|
||||
).validFull() // apDomainが ? だった場合など
|
||||
}
|
||||
hostDomain2: HostAndDomain? = null,
|
||||
): Acct? {
|
||||
// 最初から有効なfull acctがあればそれを使う
|
||||
if (rawAcct.isValidFull) return rawAcct
|
||||
|
||||
// (MFMのみ)URLがなければ引数から適当に補う
|
||||
if (url == null) {
|
||||
return (hostDomain1?.apDomain?.valid()
|
||||
?: hostDomain2?.apDomain?.valid())
|
||||
?.let { Acct.parse(rawAcct.username, it) }
|
||||
}
|
||||
|
||||
var host = TootAccount.reHostInUrl.matcher(url).findOrNull()
|
||||
?.groupEx(1)?.let { Host.parse(it).valid() }
|
||||
// URL中のホスト名が引数で指定されたドメインなら、APIホストとAPドメインの変換を行う
|
||||
when (host) {
|
||||
null -> return null
|
||||
hostDomain1?.apiHost -> host = hostDomain1.apDomain
|
||||
hostDomain2?.apiHost -> host = hostDomain2.apDomain
|
||||
}
|
||||
return Acct.parse(rawAcct.username, host).validFull()
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ object TootTextEncoder {
|
|||
context,
|
||||
accessInfo,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account,
|
||||
).decodeHTML(status.content)
|
||||
)
|
||||
|
||||
|
@ -103,7 +103,7 @@ object TootTextEncoder {
|
|||
context,
|
||||
accessInfo,
|
||||
mentions = status.mentions,
|
||||
mentionDefaultHostDomain = status.account
|
||||
authorDomain = status.account,
|
||||
).decodeHTML(status.content)
|
||||
)
|
||||
|
||||
|
@ -274,7 +274,7 @@ object TootTextEncoder {
|
|||
DecodeOptions(
|
||||
context,
|
||||
accessInfo,
|
||||
mentionDefaultHostDomain = who
|
||||
authorDomain = who,
|
||||
).decodeHTML(who.note)
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue