mirror of
https://github.com/tateisu/SubwayTooter
synced 2025-01-28 01:29:23 +01:00
account.last_status_atの解釈を変える
This commit is contained in:
parent
29fa9c8007
commit
40cad1b58f
@ -85,10 +85,10 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
get() = movedRef?.get()
|
||||
|
||||
class Field(
|
||||
val name: String,
|
||||
val value: String,
|
||||
val verified_at: Long // 0L if not verified
|
||||
)
|
||||
val name: String,
|
||||
val value: String,
|
||||
val verified_at: Long // 0L if not verified
|
||||
)
|
||||
|
||||
val fields: ArrayList<Field>?
|
||||
|
||||
@ -120,7 +120,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
|
||||
// mastodon 3.0.0-dev // last_status_at : "2019-08-29T12:42:08.838Z" or null
|
||||
// mastodon 3.1 // last_status_at : "2019-08-29" or null
|
||||
private var last_status_at =0L
|
||||
private var last_status_at = 0L
|
||||
|
||||
// mastodon 3.3.0
|
||||
var suspended = false
|
||||
@ -133,150 +133,150 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
src["_fromStream"] = parser.fromStream
|
||||
|
||||
when (parser.serviceType) {
|
||||
ServiceType.MISSKEY -> {
|
||||
ServiceType.MISSKEY -> {
|
||||
|
||||
|
||||
this.custom_emojis =
|
||||
parseMapOrNull(CustomEmoji.decodeMisskey, src.jsonArray("emojis"))
|
||||
this.profile_emojis = null
|
||||
this.custom_emojis =
|
||||
parseMapOrNull(CustomEmoji.decodeMisskey, src.jsonArray("emojis"))
|
||||
this.profile_emojis = null
|
||||
|
||||
this.username = src.stringOrThrow("username")
|
||||
this.username = src.stringOrThrow("username")
|
||||
|
||||
this.apiHost = src.string("host")?.let { Host.parse(it) } ?: parser.apiHost
|
||||
this.apiHost = src.string("host")?.let { Host.parse(it) } ?: parser.apiHost
|
||||
|
||||
this.url = "https://${apiHost.ascii}/@$username"
|
||||
this.url = "https://${apiHost.ascii}/@$username"
|
||||
|
||||
this.apDomain = apiHost // FIXME apiHostとapDomainが異なる場合はMisskeyだとどうなの…?
|
||||
this.apDomain = apiHost // FIXME apiHostとapDomainが異なる場合はMisskeyだとどうなの…?
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
this.acct = when {
|
||||
// アクセス元から見て内部ユーザなら short acct
|
||||
parser.linkHelper.matchHost(this) -> Acct.parse(username)
|
||||
@Suppress("LeakingThis")
|
||||
this.acct = when {
|
||||
// アクセス元から見て内部ユーザなら short acct
|
||||
parser.linkHelper.matchHost(this) -> Acct.parse(username)
|
||||
|
||||
// アクセス元から見て外部ユーザならfull acct
|
||||
else -> Acct.parse(username, apDomain)
|
||||
}
|
||||
// アクセス元から見て外部ユーザならfull acct
|
||||
else -> Acct.parse(username, apDomain)
|
||||
}
|
||||
|
||||
//
|
||||
val sv = src.string("name")
|
||||
this.display_name = if (sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
//
|
||||
val sv = src.string("name")
|
||||
this.display_name = if (sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
|
||||
//
|
||||
this.note = src.string("description")
|
||||
//
|
||||
this.note = src.string("description")
|
||||
|
||||
this.source = null
|
||||
this.movedRef = null
|
||||
this.locked = src.optBoolean("isLocked")
|
||||
this.source = null
|
||||
this.movedRef = null
|
||||
this.locked = src.optBoolean("isLocked")
|
||||
|
||||
|
||||
|
||||
this.bot = src.optBoolean("isBot", false)
|
||||
this.isCat = src.optBoolean("isCat", false)
|
||||
this.isAdmin = src.optBoolean("isAdmin", false)
|
||||
this.isPro = src.optBoolean("isPro", false)
|
||||
this.bot = src.optBoolean("isBot", false)
|
||||
this.isCat = src.optBoolean("isCat", false)
|
||||
this.isAdmin = src.optBoolean("isAdmin", false)
|
||||
this.isPro = src.optBoolean("isPro", false)
|
||||
|
||||
// this.user_hides_network = src.optBoolean("user_hides_network")
|
||||
// this.user_hides_network = src.optBoolean("user_hides_network")
|
||||
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
|
||||
|
||||
this.followers_count = src.long("followersCount") ?: -1L
|
||||
this.following_count = src.long("followingCount") ?: -1L
|
||||
this.statuses_count = src.long("notesCount") ?: -1L
|
||||
this.followers_count = src.long("followersCount") ?: -1L
|
||||
this.following_count = src.long("followingCount") ?: -1L
|
||||
this.statuses_count = src.long("notesCount") ?: -1L
|
||||
|
||||
this.created_at = src.string("createdAt")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.created_at = src.string("createdAt")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
// https://github.com/syuilo/misskey/blob/develop/src/client/scripts/get-static-image-url.ts
|
||||
fun String.getStaticImageUrl(): String? {
|
||||
val uri = this.mayUri() ?: return null
|
||||
val dummy = "${uri.encodedAuthority}${uri.encodedPath}"
|
||||
return "https://${parser.linkHelper.apiHost.ascii}/proxy/$dummy?url=${encodePercent()}&static=1"
|
||||
}
|
||||
// https://github.com/syuilo/misskey/blob/develop/src/client/scripts/get-static-image-url.ts
|
||||
fun String.getStaticImageUrl(): String? {
|
||||
val uri = this.mayUri() ?: return null
|
||||
val dummy = "${uri.encodedAuthority}${uri.encodedPath}"
|
||||
return "https://${parser.linkHelper.apiHost.ascii}/proxy/$dummy?url=${encodePercent()}&static=1"
|
||||
}
|
||||
|
||||
this.avatar = src.string("avatarUrl")
|
||||
this.avatar_static = src.string("avatarUrl")?.getStaticImageUrl()
|
||||
this.header = src.string("bannerUrl")
|
||||
this.header_static = src.string("bannerUrl")?.getStaticImageUrl()
|
||||
this.avatar = src.string("avatarUrl")
|
||||
this.avatar_static = src.string("avatarUrl")?.getStaticImageUrl()
|
||||
this.header = src.string("bannerUrl")
|
||||
this.header_static = src.string("bannerUrl")?.getStaticImageUrl()
|
||||
|
||||
|
||||
this.pinnedNoteIds = src.stringArrayList("pinnedNoteIds")
|
||||
if (parser.misskeyDecodeProfilePin) {
|
||||
val list = parseList(::TootStatus, parser, src.jsonArray("pinnedNotes"))
|
||||
list.forEach { it.pinned = true }
|
||||
this.pinnedNotes = if (list.isNotEmpty()) list else null
|
||||
}
|
||||
this.pinnedNoteIds = src.stringArrayList("pinnedNoteIds")
|
||||
if (parser.misskeyDecodeProfilePin) {
|
||||
val list = parseList(::TootStatus, parser, src.jsonArray("pinnedNotes"))
|
||||
list.forEach { it.pinned = true }
|
||||
this.pinnedNotes = if (list.isNotEmpty()) list else null
|
||||
}
|
||||
|
||||
val profile = src.jsonObject("profile")
|
||||
this.location = profile?.string("location")
|
||||
this.birthday = profile?.string("birthday")
|
||||
val profile = src.jsonObject("profile")
|
||||
this.location = profile?.string("location")
|
||||
this.birthday = profile?.string("birthday")
|
||||
|
||||
|
||||
this.fields = parseMisskeyFields(src)
|
||||
this.fields = parseMisskeyFields(src)
|
||||
|
||||
|
||||
UserRelation.fromAccount(parser, src, id)
|
||||
UserRelation.fromAccount(parser, src, id)
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
MisskeyAccountDetailMap.fromAccount(parser, this, id)
|
||||
@Suppress("LeakingThis")
|
||||
MisskeyAccountDetailMap.fromAccount(parser, this, id)
|
||||
|
||||
}
|
||||
ServiceType.NOTESTOCK -> {
|
||||
}
|
||||
ServiceType.NOTESTOCK -> {
|
||||
|
||||
// notestock はActivityPub 準拠のサービスなので、サーバ内IDというのは特にない
|
||||
this.id = EntityId.DEFAULT
|
||||
// notestock はActivityPub 準拠のサービスなので、サーバ内IDというのは特にない
|
||||
this.id = EntityId.DEFAULT
|
||||
|
||||
this.username = src.stringOrThrow("display_name") // notestockはdisplay_nameとusernameが入れ替わってる?
|
||||
this.display_name = src.stringOrThrow("username")
|
||||
this.username = src.stringOrThrow("display_name") // notestockはdisplay_nameとusernameが入れ替わってる?
|
||||
this.display_name = src.stringOrThrow("username")
|
||||
|
||||
val tmpAcct = src.string("subject")?.let { Acct.parse(it) }
|
||||
val apDomain = tmpAcct?.takeIf { it.isValidFull }?.host
|
||||
?: Host.parse(
|
||||
src.string("id").mayUri()?.authority?.notEmpty()
|
||||
?: error("can't get apDomain from account's AP id.")
|
||||
)
|
||||
this.url = src.string("url")
|
||||
val apiHost = Host.parse(
|
||||
url.mayUri()?.authority?.notEmpty()
|
||||
?: error("can't get apiHost from account's AP url.")
|
||||
)
|
||||
val tmpAcct = src.string("subject")?.let { Acct.parse(it) }
|
||||
val apDomain = tmpAcct?.takeIf { it.isValidFull }?.host
|
||||
?: Host.parse(
|
||||
src.string("id").mayUri()?.authority?.notEmpty()
|
||||
?: error("can't get apDomain from account's AP id.")
|
||||
)
|
||||
this.url = src.string("url")
|
||||
val apiHost = Host.parse(
|
||||
url.mayUri()?.authority?.notEmpty()
|
||||
?: error("can't get apiHost from account's AP url.")
|
||||
)
|
||||
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
this.acct = Acct.parse(this.username, apDomain)
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
this.acct = Acct.parse(this.username, apDomain)
|
||||
|
||||
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
|
||||
this.locked = src.boolean("manuallyApprovesFollowers") ?: false
|
||||
this.locked = src.boolean("manuallyApprovesFollowers") ?: false
|
||||
|
||||
this.note = src.string("note")
|
||||
this.note = src.string("note")
|
||||
|
||||
val apTag = APTag(parser,src.jsonArray("tag"))
|
||||
this.custom_emojis = apTag.emojiList.notEmpty()
|
||||
this.profile_emojis = apTag.profileEmojiList.notEmpty()
|
||||
val apTag = APTag(parser, src.jsonArray("tag"))
|
||||
this.custom_emojis = apTag.emojiList.notEmpty()
|
||||
this.profile_emojis = apTag.profileEmojiList.notEmpty()
|
||||
|
||||
// APだと attachment にデータはあるが、検索結果に表示しないので読まない
|
||||
this.fields = null
|
||||
// APだと attachment にデータはあるが、検索結果に表示しないので読まない
|
||||
this.fields = null
|
||||
|
||||
this.source = null
|
||||
this.movedRef = null
|
||||
this.source = null
|
||||
this.movedRef = null
|
||||
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
|
||||
this.bot = false
|
||||
this.isCat = false
|
||||
this.isAdmin = false
|
||||
this.isPro = false
|
||||
}
|
||||
this.bot = false
|
||||
this.isCat = false
|
||||
this.isAdmin = false
|
||||
this.isPro = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
@ -284,8 +284,8 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
this.custom_emojis = parseMapOrNull(CustomEmoji.decode, src.jsonArray("emojis"))
|
||||
|
||||
this.profile_emojis = when (val o = src["profile_emojis"]) {
|
||||
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, TootStatus.log)
|
||||
is JsonObject -> parseProfileEmoji2(::NicoProfileEmoji, o, TootStatus.log)
|
||||
is JsonArray -> parseMapOrNull(::NicoProfileEmoji, o, TootStatus.log)
|
||||
is JsonObject -> parseProfileEmoji2(::NicoProfileEmoji, o, TootStatus.log)
|
||||
else -> null
|
||||
}
|
||||
|
||||
@ -302,11 +302,11 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
|
||||
this.source = parseSource(src.jsonObject("source"))
|
||||
this.movedRef = TootAccountRef.mayNull(
|
||||
parser,
|
||||
src.jsonObject("moved")?.let {
|
||||
TootAccount(parser, it)
|
||||
}
|
||||
)
|
||||
parser,
|
||||
src.jsonObject("moved")?.let {
|
||||
TootAccount(parser, it)
|
||||
}
|
||||
)
|
||||
this.locked = src.optBoolean("locked")
|
||||
|
||||
this.fields = parseFields(src.jsonArray("fields"))
|
||||
@ -321,91 +321,91 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
this.last_status_at = TootStatus.parseTime(src.string("last_status_at"))
|
||||
|
||||
when (parser.serviceType) {
|
||||
ServiceType.MASTODON -> {
|
||||
ServiceType.MASTODON -> {
|
||||
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
|
||||
val tmpAcct = src.stringOrThrow("acct")
|
||||
val tmpAcct = src.stringOrThrow("acct")
|
||||
|
||||
val (apiHost, apDomain) = findHostFromUrl(tmpAcct, parser.linkHelper, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
val (apiHost, apDomain) = findHostFromUrl(tmpAcct, parser.linkHelper, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
|
||||
this.acct = Acct.parse(username, if (tmpAcct.contains('@')) apDomain else null)
|
||||
this.acct = Acct.parse(username, if (tmpAcct.contains('@')) apDomain else null)
|
||||
|
||||
this.followers_count = src.long("followers_count")
|
||||
this.following_count = src.long("following_count")
|
||||
this.statuses_count = src.long("statuses_count")
|
||||
this.followers_count = src.long("followers_count")
|
||||
this.following_count = src.long("following_count")
|
||||
this.statuses_count = src.long("statuses_count")
|
||||
|
||||
this.created_at = src.string("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.created_at = src.string("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
|
||||
this.id = EntityId.DEFAULT
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
|
||||
this.id = EntityId.DEFAULT
|
||||
|
||||
val tmpAcct = src.stringOrThrow("acct")
|
||||
val tmpAcct = src.stringOrThrow("acct")
|
||||
|
||||
val (apiHost, apDomain) = findHostFromUrl(tmpAcct, null, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
val (apiHost, apDomain) = findHostFromUrl(tmpAcct, null, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apDomain
|
||||
|
||||
this.acct = Acct.parse(this.username, this.apDomain)
|
||||
this.acct = Acct.parse(this.username, this.apDomain)
|
||||
|
||||
this.followers_count = src.long("followers_count")
|
||||
this.following_count = src.long("following_count")
|
||||
this.statuses_count = src.long("statuses_count")
|
||||
this.followers_count = src.long("followers_count")
|
||||
this.following_count = src.long("following_count")
|
||||
this.statuses_count = src.long("statuses_count")
|
||||
|
||||
this.created_at = src.string("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.created_at = src.string("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
}
|
||||
this.avatar = src.string("avatar")
|
||||
this.avatar_static = src.string("avatar_static")
|
||||
this.header = src.string("header")
|
||||
this.header_static = src.string("header_static")
|
||||
}
|
||||
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
ServiceType.MSP -> {
|
||||
this.id = EntityId.mayDefault(src.string("id"))
|
||||
|
||||
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
|
||||
val (apiHost, apDomain) = findHostFromUrl(null, null, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
|
||||
val (apiHost, apDomain) = findHostFromUrl(null, null, url)
|
||||
apiHost ?: error("can't get apiHost from acct or url")
|
||||
apDomain ?: error("can't get apDomain from acct or url")
|
||||
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apiHost
|
||||
this.apiHost = apiHost
|
||||
this.apDomain = apiHost
|
||||
|
||||
this.acct = Acct.parse(this.username, this.apDomain)
|
||||
this.acct = Acct.parse(this.username, this.apDomain)
|
||||
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
|
||||
val avatar = src.string("avatar")
|
||||
this.avatar = avatar
|
||||
this.avatar_static = avatar
|
||||
this.header = null
|
||||
this.header_static = null
|
||||
val avatar = src.string("avatar")
|
||||
this.avatar = avatar
|
||||
this.avatar_static = avatar
|
||||
this.header = null
|
||||
this.header_static = null
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ServiceType.MISSKEY, ServiceType.NOTESTOCK -> error("will not happen")
|
||||
ServiceType.MISSKEY, ServiceType.NOTESTOCK -> error("will not happen")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -444,17 +444,17 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
|
||||
// decode emoji code
|
||||
return DecodeOptions(
|
||||
context,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
mentionDefaultHostDomain = this
|
||||
).decodeEmoji(sv)
|
||||
context,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
mentionDefaultHostDomain = this
|
||||
).decodeEmoji(sv)
|
||||
}
|
||||
|
||||
private fun SpannableStringBuilder.replaceAllEx(
|
||||
pattern: Pattern,
|
||||
replacement: String
|
||||
): SpannableStringBuilder {
|
||||
pattern: Pattern,
|
||||
replacement: String
|
||||
): SpannableStringBuilder {
|
||||
val m = pattern.matcher(this)
|
||||
var buffer: SpannableStringBuilder? = null
|
||||
var lastEnd = 0
|
||||
@ -483,11 +483,11 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
}
|
||||
|
||||
fun setAccountExtra(
|
||||
accessInfo: SavedAccount,
|
||||
tv: TextView,
|
||||
invalidator: NetworkEmojiInvalidator?,
|
||||
fromProfileHeader: Boolean = false
|
||||
): SpannableStringBuilder? {
|
||||
accessInfo: SavedAccount,
|
||||
tv: TextView,
|
||||
invalidator: NetworkEmojiInvalidator?,
|
||||
fromProfileHeader: Boolean = false
|
||||
): SpannableStringBuilder? {
|
||||
val pref = App1.pref
|
||||
val context = tv.context
|
||||
|
||||
@ -499,7 +499,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
prepareSb()
|
||||
.append(context.getString(R.string.last_active))
|
||||
.append(delm)
|
||||
.append(TootStatus.formatTime(context, last_status_at, true))
|
||||
.append(TootStatus.formatTime(context, last_status_at, bAllowRelative = true, onlyDate = true))
|
||||
|
||||
if (!fromProfileHeader) {
|
||||
if (Pref.bpDirectoryTootCount(pref)
|
||||
@ -519,25 +519,25 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
|
||||
if (Pref.bpDirectoryNote(pref) && note?.isNotEmpty() == true) {
|
||||
val decodedNote = DecodeOptions(
|
||||
context,
|
||||
accessInfo,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
mentionDefaultHostDomain = this,
|
||||
).decodeHTML(note)
|
||||
context,
|
||||
accessInfo,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
mentionDefaultHostDomain = this,
|
||||
).decodeHTML(note)
|
||||
.replaceAllEx(reNoteLineFeed, " ")
|
||||
.trimEx()
|
||||
if (decodedNote.isNotBlank()) {
|
||||
prepareSb().append(
|
||||
if (decodedNote is SpannableStringBuilder && decodedNote.length > 200) {
|
||||
decodedNote.replace(200, decodedNote.length, "…")
|
||||
} else {
|
||||
decodedNote
|
||||
}
|
||||
)
|
||||
if (decodedNote is SpannableStringBuilder && decodedNote.length > 200) {
|
||||
decodedNote.replace(200, decodedNote.length, "…")
|
||||
} else {
|
||||
decodedNote
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -692,10 +692,10 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
|
||||
// Tootsearch用。URLやUriを使ってアカウントのインスタンス名を調べる
|
||||
fun findHostFromUrl(
|
||||
acctArg: String?,
|
||||
linkHelper: LinkHelper?,
|
||||
url: String?
|
||||
): Pair<Host?, Host?> {
|
||||
acctArg: String?,
|
||||
linkHelper: LinkHelper?,
|
||||
url: String?
|
||||
): Pair<Host?, Host?> {
|
||||
val apDomain = findApDomain(acctArg, linkHelper)
|
||||
val apiHost = findApiHost(url)
|
||||
return Pair(apiHost ?: apDomain, apDomain ?: apiHost)
|
||||
@ -709,7 +709,7 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
val name = item.string("name") ?: continue
|
||||
val value = item.string("value") ?: continue
|
||||
val verifiedAt = when (val svVerifiedAt = item.string("verified_at")) {
|
||||
null -> 0L
|
||||
null -> 0L
|
||||
else -> TootStatus.parseTime(svVerifiedAt)
|
||||
}
|
||||
dst.add(Field(name, value, verifiedAt))
|
||||
@ -745,30 +745,30 @@ open class TootAccount(parser: TootParser, src: JsonObject) : HostAndDomain {
|
||||
runCatching {
|
||||
src.jsonObject("twitter")?.let {
|
||||
appendField(
|
||||
"Twitter",
|
||||
"@${it.string("screenName")}",
|
||||
"https://twitter.com/intent/user?user_id=${it.string("userId")}"
|
||||
)
|
||||
"Twitter",
|
||||
"@${it.string("screenName")}",
|
||||
"https://twitter.com/intent/user?user_id=${it.string("userId")}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
runCatching {
|
||||
src.jsonObject("github")?.string("login")?.let {
|
||||
appendField(
|
||||
"GitHub",
|
||||
it,
|
||||
"https://github.com/$it"
|
||||
)
|
||||
"GitHub",
|
||||
it,
|
||||
"https://github.com/$it"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
runCatching {
|
||||
src.jsonObject("discord")?.let {
|
||||
appendField(
|
||||
"Discord",
|
||||
"${it.string("username")}#${it.string("discriminator")}",
|
||||
"https://discordapp.com/users/${it.string("id")}"
|
||||
)
|
||||
"Discord",
|
||||
"${it.string("username")}#${it.string("discriminator")}",
|
||||
"https://discordapp.com/users/${it.string("id")}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,13 +487,13 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
|
||||
this.reblog = null
|
||||
|
||||
this.card = if ( quote != null) {
|
||||
this.card = if (quote != null) {
|
||||
// 引用Renoteにプレビューカードをでっちあげる
|
||||
TootCard(parser, quote)
|
||||
// content中のQTの表現が四角括弧の有無とか色々あるみたいだし
|
||||
// 選択してコピーのことを考えたらむしろ削らない方が良い気がしてきた
|
||||
// removeQt = ! Pref.bpDontShowPreviewCard(Pref.pref(parser.context))
|
||||
}else{
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@ -534,7 +534,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
mentionDefaultHostDomain = account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
)
|
||||
|
||||
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)
|
||||
@ -543,7 +543,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
if (this.highlightSpeech == null) this.highlightSpeech = options.highlightSpeech
|
||||
if (this.highlightAny == null) this.highlightAny = options.highlightAny
|
||||
|
||||
this.enquete = (src.jsonArray("oneOf")?: src.jsonArray("anyOf")) ?.let {
|
||||
this.enquete = (src.jsonArray("oneOf") ?: src.jsonArray("anyOf"))?.let {
|
||||
try {
|
||||
TootPolls(
|
||||
parser,
|
||||
@ -706,8 +706,8 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
sv.replace(reQuoteTootRemover) {
|
||||
it.groupValues.elementAtOrNull(1) ?: ""
|
||||
}.also { after ->
|
||||
log.d("removeQt? after = $after")
|
||||
}
|
||||
log.d("removeQt? after = $after")
|
||||
}
|
||||
} else
|
||||
sv
|
||||
}
|
||||
@ -985,7 +985,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
}
|
||||
}
|
||||
|
||||
fun markDeleted(context: Context, deletedAt: Long?): Boolean? {
|
||||
fun markDeleted(context: Context, deletedAt: Long?): Boolean {
|
||||
|
||||
if (Pref.bpDontRemoveDeletedToot(App1.getAppState(context).pref)) return false
|
||||
|
||||
@ -1112,12 +1112,11 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private val tz_utc = TimeZone.getTimeZone("UTC")
|
||||
|
||||
private val reDate = """\A\d+\D+\d+\D+\d+\z""".asciiPattern()
|
||||
private val reDate = """\A\d+\D+\d+\D+\d+\z""".asciiPattern()
|
||||
|
||||
private val reTime = """\A(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)(?:\D+(\d+))?"""
|
||||
private val reTime = """\A(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)(?:\D+(\d+))?"""
|
||||
.asciiPattern()
|
||||
|
||||
private val reMSPTime = """\A(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)"""
|
||||
@ -1127,9 +1126,9 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
if (strTime != null && strTime.isNotEmpty()) {
|
||||
try {
|
||||
var m = reTime.matcher(strTime)
|
||||
if (m.find()) {
|
||||
val g = GregorianCalendar(tz_utc)
|
||||
g.set(
|
||||
if (m.find()) {
|
||||
val g = GregorianCalendar(tz_utc)
|
||||
g.set(
|
||||
m.groupEx(1).optInt() ?: 1,
|
||||
(m.groupEx(2).optInt() ?: 1) - 1,
|
||||
m.groupEx(3).optInt() ?: 1,
|
||||
@ -1137,14 +1136,14 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
m.groupEx(5).optInt() ?: 0,
|
||||
m.groupEx(6).optInt() ?: 0
|
||||
)
|
||||
g.set(Calendar.MILLISECOND, m.groupEx(7).optInt() ?: 0)
|
||||
return g.timeInMillis
|
||||
}
|
||||
// last_status_at などでは YYYY-MM-DD になることがある
|
||||
m = reDate.matcher(strTime)
|
||||
if (m.find()) return parseTime("${strTime}T00:00:00.000Z")
|
||||
g.set(Calendar.MILLISECOND, m.groupEx(7).optInt() ?: 0)
|
||||
return g.timeInMillis
|
||||
}
|
||||
// last_status_at などでは YYYY-MM-DD になることがある
|
||||
m = reDate.matcher(strTime)
|
||||
if (m.find()) return parseTime("${strTime}T00:00:00.000Z")
|
||||
|
||||
log.w("invalid time format: %s", strTime)
|
||||
log.w("invalid time format: %s", strTime)
|
||||
} catch (ex: Throwable) { // ParseException, ArrayIndexOutOfBoundsException
|
||||
log.trace(ex)
|
||||
log.e(ex, "TootStatus.parseTime failed. src=%s", strTime)
|
||||
@ -1187,26 +1186,40 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
internal val date_format2 = SimpleDateFormat("yyyy-MM-dd")
|
||||
|
||||
fun formatTime(context: Context, t: Long, bAllowRelative: Boolean): String {
|
||||
if (bAllowRelative && Pref.bpRelativeTimestamp(App1.pref)) {
|
||||
val now = System.currentTimeMillis()
|
||||
var delta = now - t
|
||||
fun formatTime(context: Context, t: Long, bAllowRelative: Boolean, onlyDate: Boolean = false): String {
|
||||
|
||||
@StringRes val phraseId = if (delta >= 0)
|
||||
R.string.relative_time_phrase_past
|
||||
else
|
||||
R.string.relative_time_phrase_future
|
||||
val now = System.currentTimeMillis()
|
||||
var delta = now - t
|
||||
|
||||
@StringRes val phraseId = if (delta >= 0)
|
||||
R.string.relative_time_phrase_past
|
||||
else
|
||||
R.string.relative_time_phrase_future
|
||||
|
||||
fun f(v: Long, unit1: Int, units: Int): String {
|
||||
val vi = v.toInt()
|
||||
return context.getString(
|
||||
phraseId,
|
||||
vi,
|
||||
context.getString(if (vi <= 1) unit1 else units)
|
||||
)
|
||||
}
|
||||
|
||||
if( onlyDate) return when{
|
||||
delta < 40 * 86400000L -> f(
|
||||
delta / 86400000L,
|
||||
R.string.relative_time_unit_day1,
|
||||
R.string.relative_time_unit_days
|
||||
)
|
||||
else ->
|
||||
formatDate(t, date_format2, omitZeroSecond = false, omitYear = true)
|
||||
}
|
||||
|
||||
if (bAllowRelative && Pref.bpRelativeTimestamp(App1.pref)) {
|
||||
|
||||
delta = abs(delta)
|
||||
|
||||
fun f(v: Long, unit1: Int, units: Int): String {
|
||||
val vi = v.toInt()
|
||||
return context.getString(
|
||||
phraseId,
|
||||
vi,
|
||||
context.getString(if (vi <= 1) unit1 else units)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
when {
|
||||
delta < 1000L -> return context.getString(R.string.time_within_second)
|
||||
@ -1229,16 +1242,13 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
R.string.relative_time_unit_hours
|
||||
)
|
||||
|
||||
delta < 40 * 86400000L -> return f(
|
||||
delta < 40 * 86400000L -> return f(
|
||||
delta / 86400000L,
|
||||
R.string.relative_time_unit_day1,
|
||||
R.string.relative_time_unit_days
|
||||
)
|
||||
|
||||
else -> {
|
||||
// fall back to absolute time
|
||||
}
|
||||
}
|
||||
// fall back to absolute time
|
||||
}
|
||||
|
||||
return formatDate(t, date_format, omitZeroSecond = false, omitYear = false)
|
||||
@ -1262,10 +1272,9 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
||||
if (omitYear) {
|
||||
val dateNow = format.format(Date())
|
||||
val delm = dateNow.indexOf('-')
|
||||
if (delm != -1 && dateNow.substring(0, delm + 1) == dateTarget.substring(
|
||||
0,
|
||||
delm + 1
|
||||
)) {
|
||||
if (delm != -1 &&
|
||||
dateNow.substring(0, delm + 1) == dateTarget.substring(0,delm + 1)
|
||||
) {
|
||||
dateTarget = dateTarget.substring(delm + 1)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user