SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/action/Action_Tag.kt

280 lines
10 KiB
Kotlin
Raw Normal View History

package jp.juggler.subwaytooter.action
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.R
2021-06-28 09:09:00 +02:00
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
2021-06-22 10:31:51 +02:00
import jp.juggler.subwaytooter.api.entity.TootTag
import jp.juggler.subwaytooter.api.runApiTask
2021-06-28 09:09:00 +02:00
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")
2021-06-22 10:31:51 +02:00
fun ActMain.longClickTootTag(pos: Int, accessInfo: SavedAccount, item: TootTag) {
tagTimelineFromAccount(
pos = pos,
url = "https://${accessInfo.apiHost.ascii}/tags/${item.name.encodePercent()}",
host = accessInfo.apiHost,
tagWithoutSharp = item.name
)
}
// ハッシュタグへの操作を選択する
fun ActMain.tagDialog(
accessInfo: SavedAccount?,
pos: Int,
// タグのURL
// URLのホスト部分は普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
url: String,
// タグのHost。
// 普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
host: Host,
// タグの名前
tagWithoutSharp: String,
// 複数のタグをまとめて引用したい場合がある
tagList: ArrayList<String>? = null,
// nullでなければ投稿者別タグTLを開く選択肢を表示する
whoAcct: Acct? = null,
// TootTagの情報があれば
tagInfo: TootTag? = null,
) {
val activity = this
val tagWithSharp = "#$tagWithoutSharp"
launchMain {
try {
val d = ActionsDialog()
.addAction(getString(R.string.open_hashtag_column)) {
tagTimelineFromAccount(
pos,
url,
host,
tagWithoutSharp
)
}
// 投稿者別タグTL
if (whoAcct != null) {
d.addAction(
AcctColor.getStringWithNickname(
activity,
R.string.open_hashtag_from_account,
whoAcct
)
) {
tagTimelineFromAccount(
pos,
"https://${whoAcct.host?.ascii}/@${whoAcct.username}/tagged/${tagWithoutSharp.encodePercent()}",
host,
tagWithoutSharp,
whoAcct
)
}
}
d.addAction(getString(R.string.open_in_browser)) { openCustomTab(url) }
.addAction(getString(R.string.quote_hashtag_of,
tagWithSharp)) { openPost("$tagWithSharp ") }
if (tagList != null && tagList.size > 1) {
val sb = StringBuilder()
for (s in tagList) {
if (sb.isNotEmpty()) sb.append(' ')
sb.append(s)
}
val tagAll = sb.toString()
d.addAction(
getString(
R.string.quote_all_hashtag_of,
tagAll
)
) { openPost("$tagAll ") }
}
val ti = TootInstance.getCached(accessInfo)
if (ti != null && accessInfo?.isMisskey == false) {
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 toggle = !(tag?.following ?: false)
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)
}
}
2022-07-21 02:06:26 +02:00
d.show(activity, tagWithSharp)
} catch (ex: Throwable) {
log.e(ex, "tagDialog failed.")
}
}
}
// 検索カラムからハッシュタグを選んだ場合、カラムのアカウントでハッシュタグを開く
fun ActMain.tagTimeline(
pos: Int,
accessInfo: SavedAccount,
tagWithoutSharp: String,
acctAscii: String? = null,
) {
if (acctAscii == null) {
addColumn(pos, accessInfo, ColumnType.HASHTAG, tagWithoutSharp)
} else {
addColumn(
pos,
accessInfo,
ColumnType.HASHTAG_FROM_ACCT,
tagWithoutSharp,
acctAscii
)
}
}
// アカウントを選んでハッシュタグカラムを開く
fun ActMain.tagTimelineFromAccount(
pos: Int,
// タグのURL
// URLのホスト部分は普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
url: String,
// タグのHost。
// 普通はaccessInfoと同じだが、検索などでバラバラになる場合がある
host: Host,
// タグの名前。#を含まない
tagWithoutSharp: String,
// 「投稿者別タグTL」を開くなら、投稿者のacctを指定する
acct: Acct? = null,
) {
val dialog = ActionsDialog()
val accountList = SavedAccount.loadAccountList(this)
SavedAccount.sort(accountList)
// 分類する
val listOriginal = ArrayList<SavedAccount>()
val listOriginalPseudo = ArrayList<SavedAccount>()
val listOther = ArrayList<SavedAccount>()
for (a in accountList) {
if (acct == null) {
when {
!a.matchHost(host) -> listOther.add(a)
a.isPseudo -> listOriginalPseudo.add(a)
else -> listOriginal.add(a)
}
} else {
when {
// 疑似アカウントはacctからaccount idを取得できないので
// アカウント別タグTLを開けない
a.isPseudo -> Unit
// ミスキーはアカウント別タグTLがないので
// アカウント別タグTLを開けない
a.isMisskey -> Unit
!a.matchHost(host) -> listOther.add(a)
else -> listOriginal.add(a)
}
}
}
// ブラウザで表示する
dialog.addAction(getString(R.string.open_web_on_host, host)) {
openCustomTab(url)
}
// 同タンスのアカウントがない場合は疑似アカウントを作成して開く
// ただし疑似アカウントではアカウントの同期ができないため、特定ユーザのタグTLは読めない)
if (acct == null && listOriginal.isEmpty() && listOriginalPseudo.isEmpty()) {
dialog.addAction(getString(R.string.open_in_pseudo_account, "?@$host")) {
launchMain {
addPseudoAccount(host)?.let { tagTimeline(pos, it, tagWithoutSharp) }
}
}
}
// 分類した順に選択肢を追加する
for (a in listOriginal) {
dialog.addAction(
AcctColor.getStringWithNickname(
this,
R.string.open_in_account,
a.acct
)
) {
tagTimeline(pos, a, tagWithoutSharp, acct?.ascii)
}
}
for (a in listOriginalPseudo) {
dialog.addAction(AcctColor.getStringWithNickname(this, R.string.open_in_account, a.acct)) {
tagTimeline(pos, a, tagWithoutSharp, acct?.ascii)
}
}
for (a in listOther) {
dialog.addAction(AcctColor.getStringWithNickname(this, R.string.open_in_account, a.acct)) {
tagTimeline(pos, a, tagWithoutSharp, acct?.ascii)
}
}
dialog.show(this, "#$tagWithoutSharp")
}
fun ActMain.followHashTag(
accessInfo: SavedAccount,
tagWithoutSharp: String,
isSet: Boolean,
) {
val activity = this
launchMain {
runApiTask(accessInfo) { client ->
client.request(
"/api/v1/tags/${tagWithoutSharp.encodePercent()}/${if (isSet) "follow" else "unfollow"}",
"".toFormRequestBody().toPost()
)
}?.let { result ->
when (val error = result.error) {
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)
}
}
}
}