タグのフォロー操作の結果をフォロータグ一覧やトレンドタグや検索結果のカラムに反映させる。それらのカラムにタグのフォロー中状態を表示する。
This commit is contained in:
parent
b148c6dd03
commit
1de061a5b1
|
@ -3,18 +3,22 @@ package jp.juggler.subwaytooter.action
|
|||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.actmain.addColumn
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.api.entity.Acct
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.api.entity.TootTag
|
||||
import jp.juggler.subwaytooter.api.runApiTask
|
||||
import jp.juggler.subwaytooter.column.ColumnType
|
||||
import jp.juggler.subwaytooter.column.onTagFollowChanged
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.global.appDispatchers
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.matchHost
|
||||
import jp.juggler.subwaytooter.util.openCustomTab
|
||||
import jp.juggler.util.*
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private val log = LogCategory("Action_Tag")
|
||||
|
||||
|
@ -31,14 +35,23 @@ fun ActMain.longClickTootTag(pos: Int, accessInfo: SavedAccount, item: TootTag)
|
|||
fun ActMain.tagDialog(
|
||||
accessInfo: SavedAccount?,
|
||||
pos: Int,
|
||||
// タグのURL
|
||||
// URLのホスト部分は普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
|
||||
url: String,
|
||||
// タグのHost。
|
||||
// 普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
|
||||
host: Host,
|
||||
// タグの名前
|
||||
tagWithoutSharp: String,
|
||||
tagList: ArrayList<String>?,
|
||||
whoAcct: Acct?,
|
||||
// 複数のタグをまとめて引用したい場合がある
|
||||
tagList: ArrayList<String>? = null,
|
||||
// nullでなければ投稿者別タグTLを開く選択肢を表示する
|
||||
whoAcct: Acct? = null,
|
||||
// TootTagの情報があれば
|
||||
tagInfo: TootTag? = null,
|
||||
) {
|
||||
val activity = this
|
||||
val tagWithSharp = "#$tagWithoutSharp"
|
||||
|
||||
launchMain {
|
||||
try {
|
||||
|
||||
|
@ -52,12 +65,11 @@ fun ActMain.tagDialog(
|
|||
)
|
||||
}
|
||||
|
||||
// https://mastodon.juggler.jp/@tateisu/101865456016473337
|
||||
// 一時的に使えなくする
|
||||
// 投稿者別タグTL
|
||||
if (whoAcct != null) {
|
||||
d.addAction(
|
||||
AcctColor.getStringWithNickname(
|
||||
this@tagDialog,
|
||||
activity,
|
||||
R.string.open_hashtag_from_account,
|
||||
whoAcct
|
||||
)
|
||||
|
@ -93,22 +105,23 @@ fun ActMain.tagDialog(
|
|||
|
||||
val ti = TootInstance.getCached(accessInfo)
|
||||
if (ti != null && accessInfo?.isMisskey == false) {
|
||||
val result = runApiTask(accessInfo) { client ->
|
||||
client.request("/api/v1/tags/${tagWithoutSharp.encodePercent()}")
|
||||
var tag = tagInfo
|
||||
if (tag == null) {
|
||||
val result = runApiTask(accessInfo) { client ->
|
||||
client.request("/api/v1/tags/${tagWithoutSharp.encodePercent()}")
|
||||
} ?: return@launchMain //cancelled.
|
||||
TootParser(activity, accessInfo)
|
||||
.tag(result.jsonObject)
|
||||
?.let { tag = it }
|
||||
}
|
||||
val following = when {
|
||||
result == null || result.error != null -> null
|
||||
else -> result.jsonObject?.boolean("following")
|
||||
|
||||
val toggle = !(tag?.following ?: false)
|
||||
val toggleCaption = when (toggle) {
|
||||
true -> R.string.follow_hashtag_of
|
||||
else -> R.string.unfollow_hashtag_of
|
||||
}
|
||||
val toggle = following?.let { !it }
|
||||
if (toggle != null) {
|
||||
val toggleCaption = when (toggle) {
|
||||
true -> R.string.follow_hashtag_of
|
||||
else -> R.string.unfollow_hashtag_of
|
||||
}
|
||||
d.addAction(getString(toggleCaption, tagWithSharp)) {
|
||||
followHashTag(accessInfo, tagWithoutSharp, toggle)
|
||||
}
|
||||
d.addAction(getString(toggleCaption, tagWithSharp)) {
|
||||
followHashTag(accessInfo, tagWithoutSharp, toggle)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,9 +155,15 @@ fun ActMain.tagTimeline(
|
|||
// アカウントを選んでハッシュタグカラムを開く
|
||||
fun ActMain.tagTimelineFromAccount(
|
||||
pos: Int,
|
||||
// タグのURL
|
||||
// URLのホスト部分は普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
|
||||
url: String,
|
||||
// タグのHost。
|
||||
// 普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
|
||||
host: Host,
|
||||
// タグの名前。#を含まない
|
||||
tagWithoutSharp: String,
|
||||
// 「投稿者別タグTL」を開くなら、投稿者のacctを指定する
|
||||
acct: Acct? = null,
|
||||
) {
|
||||
|
||||
|
@ -166,14 +185,13 @@ fun ActMain.tagTimelineFromAccount(
|
|||
}
|
||||
} else {
|
||||
when {
|
||||
// 疑似アカウントはacctからaccount idを取得できないので
|
||||
// アカウント別タグTLを開けない
|
||||
a.isPseudo -> Unit
|
||||
|
||||
// acctからidを取得できない
|
||||
a.isPseudo -> {
|
||||
}
|
||||
|
||||
// ミスキーのアカウント別タグTLは未対応
|
||||
a.isMisskey -> {
|
||||
}
|
||||
// ミスキーはアカウント別タグTLがないので
|
||||
// アカウント別タグTLを開けない
|
||||
a.isMisskey -> Unit
|
||||
|
||||
!a.matchHost(host) -> listOther.add(a)
|
||||
else -> listOriginal.add(a)
|
||||
|
@ -227,6 +245,7 @@ fun ActMain.followHashTag(
|
|||
tagWithoutSharp: String,
|
||||
isSet: Boolean,
|
||||
) {
|
||||
val activity = this
|
||||
launchMain {
|
||||
runApiTask(accessInfo) { client ->
|
||||
client.request(
|
||||
|
@ -235,14 +254,24 @@ fun ActMain.followHashTag(
|
|||
)
|
||||
}?.let { result ->
|
||||
when (val error = result.error) {
|
||||
// 成功時はTagオブジェクトが返るが、使っていない
|
||||
null -> showToast(
|
||||
false,
|
||||
when {
|
||||
isSet -> R.string.follow_succeeded
|
||||
else -> R.string.unfollow_succeeded
|
||||
null -> {
|
||||
showToast(
|
||||
false,
|
||||
when {
|
||||
isSet -> R.string.follow_succeeded
|
||||
else -> R.string.unfollow_succeeded
|
||||
}
|
||||
)
|
||||
// 成功時はTagオブジェクトが返る
|
||||
// フォロー中のタグ一覧を更新する
|
||||
TootParser(activity, accessInfo).tag(result.jsonObject)?.let { tag ->
|
||||
withContext(appDispatchers.main.immediate) {
|
||||
for (column in appState.columnList) {
|
||||
column.onTagFollowChanged(accessInfo, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> showToast(true, error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1417,7 +1417,6 @@ suspend fun TootApiClient.syncStatus(
|
|||
TootParser(
|
||||
context,
|
||||
linkHelper = LinkHelper.create(host, misskeyVersion = 10),
|
||||
serviceType = ServiceType.MISSKEY
|
||||
)
|
||||
.status(result.jsonObject)
|
||||
?.apply {
|
||||
|
|
|
@ -13,7 +13,10 @@ class TootParser(
|
|||
val linkHelper: LinkHelper,
|
||||
var pinned: Boolean = false, // プロフィールカラムからpinned TL を読んだ時だけ真
|
||||
var highlightTrie: WordTrieTree? = null,
|
||||
var serviceType: ServiceType = ServiceType.MASTODON,
|
||||
var serviceType: ServiceType = when {
|
||||
linkHelper.isMisskey -> ServiceType.MISSKEY
|
||||
else -> ServiceType.MASTODON
|
||||
},
|
||||
var misskeyDecodeProfilePin: Boolean = false,
|
||||
var fromStream: Boolean = false,
|
||||
var decodeQuote: Boolean = true,
|
||||
|
@ -28,10 +31,6 @@ class TootParser(
|
|||
val apDomain: Host
|
||||
get() = linkHelper.apDomain
|
||||
|
||||
init {
|
||||
if (linkHelper.isMisskey) serviceType = ServiceType.MISSKEY
|
||||
}
|
||||
|
||||
fun getFullAcct(acct: Acct?) = linkHelper.getFullAcct(acct)
|
||||
|
||||
fun account(src: JsonObject?) = parseItem(::TootAccount, this, src)
|
||||
|
@ -44,6 +43,9 @@ class TootParser(
|
|||
fun notification(src: JsonObject?) = parseItem(::TootNotification, this, src)
|
||||
fun notificationList(src: JsonArray?) = parseList(::TootNotification, this, src)
|
||||
|
||||
fun tag(src: JsonObject?) =
|
||||
src?.let { TootTag.parse(this, it) }
|
||||
|
||||
fun tagList(array: JsonArray?) =
|
||||
TootTag.parseList(this, array)
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ open class TootTag constructor(
|
|||
|
||||
var type: TagType = TagType.Tag,
|
||||
|
||||
// (Mastodon 3.6) タグをフォロー中なら真
|
||||
val following: Boolean? = null,
|
||||
|
||||
// The URL of the hashtag. may null if generated from TootContext
|
||||
val url: String? = null,
|
||||
|
||||
|
@ -22,12 +25,12 @@ open class TootTag constructor(
|
|||
|
||||
// Mastodon /api/v2/search provides history.
|
||||
val history: ArrayList<History>? = null,
|
||||
) : TimelineItem() {
|
||||
|
||||
) : TimelineItem() {
|
||||
|
||||
enum class TagType {
|
||||
Tag,
|
||||
TrendLink,
|
||||
FollowedTags,
|
||||
Link,
|
||||
}
|
||||
|
||||
val countDaily: Int
|
||||
|
@ -71,12 +74,12 @@ open class TootTag constructor(
|
|||
val name = src.stringOrThrow("tag").replaceFirst(reHeadSharp, "")
|
||||
TootTag(
|
||||
name = name,
|
||||
url = "https://${parser.apiHost}/tags/${Uri.encode(name)}"
|
||||
url = "https://${parser.apiHost}/tags/${Uri.encode(name)}",
|
||||
)
|
||||
}
|
||||
src.string("type") == "link" -> {
|
||||
TootTag(
|
||||
type = TagType.TrendLink,
|
||||
type = TagType.Link,
|
||||
name = src.string("title") ?: "",
|
||||
url = src.string("url") ?: "",
|
||||
description = src.string("description") ?: "",
|
||||
|
@ -95,7 +98,8 @@ open class TootTag constructor(
|
|||
url = (src.string("url") ?: src.string("href"))
|
||||
?.replaceFirst(reUserTagUrl, "/tags/")
|
||||
?.replaceFirst(reNotestockTagUrl, "/tags/"),
|
||||
history = parseHistories(src.jsonArray("history"))
|
||||
history = parseHistories(src.jsonArray("history")),
|
||||
following = src.boolean("following"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -434,3 +434,39 @@ fun replaceConversationSummary(
|
|||
if (removeSet.contains(o.id)) it.remove()
|
||||
}
|
||||
}
|
||||
|
||||
// タグのフォロー状態が変わったら呼ばれる
|
||||
fun Column.onTagFollowChanged(account: SavedAccount, newTag: TootTag) {
|
||||
|
||||
if (isDispose.get() || bInitialLoading || bRefreshLoading) return
|
||||
if (accessInfo != account) return
|
||||
|
||||
when (type) {
|
||||
ColumnType.FOLLOWED_HASHTAGS, ColumnType.TREND_TAG, ColumnType.SEARCH -> {
|
||||
val tmpList = ArrayList<TimelineItem>(listData.size)
|
||||
for (o in listData) {
|
||||
if (o is TootTag && o.name == newTag.name) {
|
||||
tmpList.add(newTag)
|
||||
} else {
|
||||
tmpList.add(o)
|
||||
}
|
||||
}
|
||||
if (type == ColumnType.FOLLOWED_HASHTAGS) {
|
||||
val tagFinder:(TimelineItem)->Boolean = {it is TootTag && it.name == newTag.name}
|
||||
when (newTag.following) {
|
||||
true ->
|
||||
if (tmpList.none(tagFinder)) {
|
||||
tmpList.add(0, newTag)
|
||||
}
|
||||
else -> tmpList.indexOfFirst(tagFinder)
|
||||
.takeIf { it>=0 }?.let{ tmpList.removeAt(it)}
|
||||
}
|
||||
}
|
||||
listData.clear()
|
||||
listData.addAll(tmpList)
|
||||
fireShowContent(reason = "onTagFollowChanged")
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -866,7 +866,6 @@ class ColumnTask_Loading(
|
|||
suspend fun getFollowedHashtags(client: TootApiClient): TootApiResult? {
|
||||
val result = client.request("/api/v1/followed_tags")
|
||||
val src = parser.tagList(result?.jsonArray)
|
||||
.onEach { it.type = TootTag.TagType.FollowedTags }
|
||||
listTmp = addAll(listTmp, src)
|
||||
column.saveRange(bBottom = true, bTop = true, result = result, list = src)
|
||||
return result
|
||||
|
@ -1217,7 +1216,7 @@ class ColumnTask_Loading(
|
|||
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(parser, query)
|
||||
if (searchResult != null) {
|
||||
listTmp = java.util.ArrayList()
|
||||
listTmp = ArrayList()
|
||||
addAll(listTmp, searchResult.hashtags)
|
||||
if (searchResult.searchApiVersion >= 2 && searchResult.hashtags.isNotEmpty()) {
|
||||
addOne(listTmp, TootSearchGap(TootSearchGap.SearchType.Hashtag))
|
||||
|
|
|
@ -1178,7 +1178,6 @@ class ColumnTask_Refresh(
|
|||
val path = column.addRange(bBottom = bBottom, "/api/v1/followed_tags")
|
||||
val result = client.request(path)
|
||||
val src = parser.tagList(result?.jsonArray)
|
||||
.onEach { it.type = TootTag.TagType.FollowedTags }
|
||||
listTmp = addAll(listTmp, src)
|
||||
column.saveRange(bBottom = bBottom, bTop = !bBottom, result = result, list = src)
|
||||
return result
|
||||
|
|
|
@ -233,7 +233,7 @@ suspend fun Column.makeHashtagAcctUrl(client: TootApiClient): String? {
|
|||
fun Column.makeMisskeyBaseParameter(parser: TootParser?) =
|
||||
accessInfo.putMisskeyApiToken().apply {
|
||||
if (accessInfo.isMisskey) {
|
||||
if (parser != null) parser.serviceType = ServiceType.MISSKEY
|
||||
parser?.serviceType = ServiceType.MISSKEY
|
||||
put("limit", 40)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,18 +265,15 @@ private fun ItemViewHolder.clickTag(pos: Int, item: TimelineItem?) {
|
|||
when (item) {
|
||||
is TootTag -> when (item.type) {
|
||||
TootTag.TagType.Tag ->
|
||||
tagTimeline(pos, accessInfo, item.name)
|
||||
TootTag.TagType.FollowedTags -> {
|
||||
val host = accessInfo.apiHost
|
||||
tagDialog(accessInfo,
|
||||
tagDialog(
|
||||
accessInfo,
|
||||
pos,
|
||||
item.url!!,
|
||||
host,
|
||||
accessInfo.apiHost,
|
||||
item.name,
|
||||
tagList = null,
|
||||
whoAcct = null)
|
||||
}
|
||||
TootTag.TagType.TrendLink ->
|
||||
tagInfo = item,
|
||||
)
|
||||
TootTag.TagType.Link ->
|
||||
openCustomTab(item.url)
|
||||
}
|
||||
is TootSearchGap -> column.startGap(item, isHead = true)
|
||||
|
|
|
@ -421,14 +421,19 @@ fun ItemViewHolder.showSearchTag(tag: TootTag) {
|
|||
tvTrendTagCount.text = "${tag.countDaily}(${tag.countWeekly})"
|
||||
cvTagHistory.setHistory(tag.history)
|
||||
when (tag.type) {
|
||||
TootTag.TagType.TrendLink -> {
|
||||
TootTag.TagType.Link -> {
|
||||
tvTrendTagName.text = tag.url?.ellipsizeDot3(256)
|
||||
tvTrendTagDesc.text = tag.name + "\n" + tag.description
|
||||
}
|
||||
else -> {
|
||||
TootTag.TagType.Tag -> {
|
||||
tvTrendTagName.text = "#${tag.name.ellipsizeDot3(256)}"
|
||||
tvTrendTagDesc.text =
|
||||
tvTrendTagDesc.text = listOf(
|
||||
when (tag.following) {
|
||||
true -> activity.getString(R.string.following)
|
||||
else -> ""
|
||||
},
|
||||
activity.getString(R.string.people_talking, tag.accountDaily, tag.accountWeekly)
|
||||
).filter { it.isNotEmpty() }.joinToString(" ")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue