1
0
mirror of https://github.com/tateisu/SubwayTooter synced 2025-01-26 16:56:28 +01:00

refactor column class

This commit is contained in:
tateisu 2021-05-17 22:20:08 +09:00
parent 7caef24a81
commit a2d91673e6
9 changed files with 1721 additions and 1731 deletions

View File

@ -13,7 +13,6 @@ import jp.juggler.subwaytooter.streaming.*
import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.BucketList
import jp.juggler.subwaytooter.util.ScrollPosition
import jp.juggler.subwaytooter.util.matchHost
import jp.juggler.util.*
import okhttp3.Handshake
import java.io.File
@ -26,7 +25,6 @@ import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
import kotlin.collections.ArrayList
import kotlin.math.max
import kotlin.math.min
class Column(
val app_state: AppState,
@ -1269,160 +1267,6 @@ class Column(
}
}
// ブーストやお気に入りの更新に使う。ステータスを列挙する。
fun findStatus(
target_apDomain: Host,
target_status_id: EntityId,
callback: (account: SavedAccount, status: TootStatus) -> Boolean
// callback return true if rebind view required
) {
if (!access_info.matchHost(target_apDomain)) return
var bChanged = false
fun procStatus(status: TootStatus?) {
if (status != null) {
if (target_status_id == status.id) {
if (callback(access_info, status)) bChanged = true
}
procStatus(status.reblog)
}
}
for (data in list_data) {
when (data) {
is TootNotification -> procStatus(data.status)
is TootStatus -> procStatus(data)
}
}
if (bChanged) fireRebindAdapterItems()
}
// ミュート、ブロックが成功した時に呼ばれる
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
fun removeAccountInTimeline(
target_account: SavedAccount,
who_id: EntityId,
removeFromUserList: Boolean = false
) {
if (target_account != access_info) return
val INVALID_ACCOUNT = -1L
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootStatus) {
if (who_id == (o.account.id)) continue
if (who_id == (o.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
} else if (o is TootNotification) {
if (who_id == (o.account?.id ?: INVALID_ACCOUNT)) continue
if (who_id == (o.status?.account?.id ?: INVALID_ACCOUNT)) continue
if (who_id == (o.status?.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
} else if (o is TootAccountRef && removeFromUserList) {
if (who_id == o.get().id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeAccountInTimeline")
}
}
// ミュート、ブロックが成功した時に呼ばれる
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
// require full acct
fun removeAccountInTimelinePseudo(acct: Acct) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootStatus) {
if (acct == access_info.getFullAcct(o.account)) continue
if (acct == access_info.getFullAcct(o.reblog?.account)) continue
} else if (o is TootNotification) {
if (acct == access_info.getFullAcct(o.account)) continue
if (acct == access_info.getFullAcct(o.status?.account)) continue
if (acct == access_info.getFullAcct(o.status?.reblog?.account)) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeAccountInTimelinePseudo")
}
}
// misskeyカラムやプロフカラムでブロック成功した時に呼ばれる
fun updateFollowIcons(target_account: SavedAccount) {
if (target_account != access_info) return
fireShowContent(reason = "updateFollowIcons", reset = true)
}
fun removeUser(targetAccount: SavedAccount, columnType: ColumnType, who_id: EntityId) {
if (type == columnType && targetAccount == access_info) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootAccountRef) {
if (o.get().id == who_id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeUser")
}
}
}
fun removeNotifications() {
cancelLastTask()
mRefreshLoadingErrorPopupState = 0
mRefreshLoadingError = ""
bRefreshLoading = false
mInitialLoadingError = ""
bInitialLoading = false
idOld = null
idRecent = null
offsetNext = 0
pagingType = ColumnPagingType.Default
list_data.clear()
duplicate_map.clear()
fireShowContent(reason = "removeNotifications", reset = true)
PollingWorker.queueNotificationCleared(context, access_info.db_id)
}
fun removeNotificationOne(target_account: SavedAccount, notification: TootNotification) {
if (!isNotificationColumn) return
if (access_info != target_account) return
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootNotification) {
if (o.id == notification.id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeNotificationOne")
}
}
internal fun addColumnViewHolder(cvh: ColumnViewHolder) {
// 現在のリストにあるなら削除する
@ -1450,9 +1294,7 @@ class Column(
}
}
internal fun hasMultipleViewHolder(): Boolean {
return _holder_list.size > 1
}
internal fun hasMultipleViewHolder(): Boolean = _holder_list.size > 1
internal fun fireShowContent(
reason: String,
@ -1519,7 +1361,6 @@ class Column(
// return null;
// }
private inner class UpdateRelationEnv {
val who_set = HashSet<EntityId>()
@ -1717,9 +1558,6 @@ class Column(
env.update(client, parser)
}
internal fun parseRange(
result: TootApiResult?,
list: List<TimelineItem>?
@ -1882,32 +1720,6 @@ class Column(
}
}
fun StringBuilder.appendHashtagExtra(): StringBuilder {
val limit = (HASHTAG_ELLIPSIZE * 2 - min(length, HASHTAG_ELLIPSIZE)) / 3
if (hashtag_any.isNotBlank()) append(' ').append(
context.getString(
R.string.hashtag_title_any,
hashtag_any.ellipsizeDot3(limit)
)
)
if (hashtag_all.isNotBlank()) append(' ').append(
context.getString(
R.string.hashtag_title_all,
hashtag_all.ellipsizeDot3(limit)
)
)
if (hashtag_none.isNotBlank()) append(' ').append(
context.getString(
R.string.hashtag_title_none,
hashtag_none.ellipsizeDot3(limit)
)
)
return this
}
fun saveScrollPosition() {
try {
if (viewHolder?.saveScrollPosition() == true) {

View File

@ -843,7 +843,7 @@ fun Column.onStatusRemoved(tl_host: Host, status_id: EntityId) {
// 既存データ中の会話サマリ項目と追加データの中にIDが同じものがあれば
// 既存データを入れ替えて追加データから削除するか
// 既存データを削除するかする
fun Column.replaceConversationSummary(
fun replaceConversationSummary(
changeList: ArrayList<AdapterChange>,
list_new: ArrayList<TimelineItem>,
list_data: BucketList<TimelineItem>
@ -1152,3 +1152,4 @@ fun Column.checkFiltersForListData(trees: FilterTrees?) {
fireShowContent(reason = "filter updated", changeList = changeList)
}

View File

@ -0,0 +1,184 @@
package jp.juggler.subwaytooter
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.notification.PollingWorker
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.matchHost
import jp.juggler.util.ellipsizeDot3
// ブーストやお気に入りの更新に使う。ステータスを列挙する。
fun Column.findStatus(
target_apDomain: Host,
target_status_id: EntityId,
callback: (account: SavedAccount, status: TootStatus) -> Boolean
// callback return true if rebind view required
) {
if (!access_info.matchHost(target_apDomain)) return
var bChanged = false
fun procStatus(status: TootStatus?) {
if (status != null) {
if (target_status_id == status.id) {
if (callback(access_info, status)) bChanged = true
}
procStatus(status.reblog)
}
}
for (data in list_data) {
when (data) {
is TootNotification -> procStatus(data.status)
is TootStatus -> procStatus(data)
}
}
if (bChanged) fireRebindAdapterItems()
}
// ミュート、ブロックが成功した時に呼ばれる
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
fun Column.removeAccountInTimeline(
target_account: SavedAccount,
who_id: EntityId,
removeFromUserList: Boolean = false
) {
if (target_account != access_info) return
val INVALID_ACCOUNT = -1L
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootStatus) {
if (who_id == (o.account.id)) continue
if (who_id == (o.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
} else if (o is TootNotification) {
if (who_id == (o.account?.id ?: INVALID_ACCOUNT)) continue
if (who_id == (o.status?.account?.id ?: INVALID_ACCOUNT)) continue
if (who_id == (o.status?.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
} else if (o is TootAccountRef && removeFromUserList) {
if (who_id == o.get().id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeAccountInTimeline")
}
}
// ミュート、ブロックが成功した時に呼ばれる
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
// require full acct
fun Column.removeAccountInTimelinePseudo(acct: Acct) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootStatus) {
if (acct == access_info.getFullAcct(o.account)) continue
if (acct == access_info.getFullAcct(o.reblog?.account)) continue
} else if (o is TootNotification) {
if (acct == access_info.getFullAcct(o.account)) continue
if (acct == access_info.getFullAcct(o.status?.account)) continue
if (acct == access_info.getFullAcct(o.status?.reblog?.account)) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeAccountInTimelinePseudo")
}
}
// misskeyカラムやプロフカラムでブロック成功した時に呼ばれる
fun Column.updateFollowIcons(target_account: SavedAccount) {
if (target_account != access_info) return
fireShowContent(reason = "updateFollowIcons", reset = true)
}
fun Column.removeUser(targetAccount: SavedAccount, columnType: ColumnType, who_id: EntityId) {
if (type == columnType && targetAccount == access_info) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootAccountRef) {
if (o.get().id == who_id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeUser")
}
}
}
fun Column.removeNotifications() {
cancelLastTask()
mRefreshLoadingErrorPopupState = 0
mRefreshLoadingError = ""
bRefreshLoading = false
mInitialLoadingError = ""
bInitialLoading = false
idOld = null
idRecent = null
offsetNext = 0
pagingType = ColumnPagingType.Default
list_data.clear()
duplicate_map.clear()
fireShowContent(reason = "removeNotifications", reset = true)
PollingWorker.queueNotificationCleared(context, access_info.db_id)
}
fun Column.removeNotificationOne(target_account: SavedAccount, notification: TootNotification) {
if (!isNotificationColumn) return
if (access_info != target_account) return
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for (o in list_data) {
if (o is TootNotification) {
if (o.id == notification.id) continue
}
tmp_list.add(o)
}
if (tmp_list.size != list_data.size) {
list_data.clear()
list_data.addAll(tmp_list)
fireShowContent(reason = "removeNotificationOne")
}
}
fun StringBuilder.appendHashtagExtra(column: Column): StringBuilder {
val limit = (Column.HASHTAG_ELLIPSIZE * 2 - kotlin.math.min(length, Column.HASHTAG_ELLIPSIZE)) / 3
if (column.hashtag_any.isNotBlank()) append(' ').append(
column.context.getString(
R.string.hashtag_title_any,
column.hashtag_any.ellipsizeDot3(limit)
)
)
if (column.hashtag_all.isNotBlank()) append(' ').append(
column.context.getString(
R.string.hashtag_title_all,
column.hashtag_all.ellipsizeDot3(limit)
)
)
if (column.hashtag_none.isNotBlank()) append(' ').append(
column.context.getString(
R.string.hashtag_title_none,
column.hashtag_none.ellipsizeDot3(limit)
)
)
return this
}

View File

@ -100,7 +100,7 @@ class ColumnTask_Gap(
val changeList = ArrayList<AdapterChange>()
column.replaceConversationSummary(changeList, list_new, column.list_data)
replaceConversationSummary(changeList, list_new, column.list_data)
val added = list_new.size // may 0

View File

@ -144,7 +144,7 @@ class ColumnTask_Refresh(
}
}
column.replaceConversationSummary(changeList, list_new, column.list_data)
replaceConversationSummary(changeList, list_new, column.list_data)
val added = list_new.size // may 0

File diff suppressed because it is too large Load Diff

View File

@ -48,9 +48,8 @@ fun SharedPreferences.Editor.remove(item : BasePref<*>) : SharedPreferences.Edit
class BooleanPref(key : String, defVal : Boolean) : BasePref<Boolean>(key, defVal) {
override operator fun invoke(pref : SharedPreferences) : Boolean {
return pref.getBoolean(key, defVal)
}
override operator fun invoke(pref : SharedPreferences) :Boolean =
pref.getBoolean(key, defVal)
// put if value is not default, remove if value is same to default
override fun put(editor : SharedPreferences.Editor, v : Boolean) {
@ -60,9 +59,8 @@ class BooleanPref(key : String, defVal : Boolean) : BasePref<Boolean>(key, defVa
class IntPref(key : String, defVal : Int) : BasePref<Int>(key, defVal) {
override operator fun invoke(pref : SharedPreferences) : Int {
return pref.getInt(key, defVal)
}
override operator fun invoke(pref : SharedPreferences) : Int =
pref.getInt(key, defVal)
override fun put(editor : SharedPreferences.Editor, v : Int) {
if(v == defVal) editor.remove(key) else editor.putInt(key, v)
@ -71,9 +69,8 @@ class IntPref(key : String, defVal : Int) : BasePref<Int>(key, defVal) {
class LongPref(key : String, defVal : Long) : BasePref<Long>(key, defVal) {
override operator fun invoke(pref : SharedPreferences) : Long {
return pref.getLong(key, defVal)
}
override operator fun invoke(pref : SharedPreferences) : Long =
pref.getLong(key, defVal)
override fun put(editor : SharedPreferences.Editor, v : Long) {
if(v == defVal) editor.remove(key) else editor.putLong(key, v)
@ -82,9 +79,8 @@ class LongPref(key : String, defVal : Long) : BasePref<Long>(key, defVal) {
class FloatPref(key : String, defVal : Float) : BasePref<Float>(key, defVal) {
override operator fun invoke(pref : SharedPreferences) : Float {
return pref.getFloat(key, defVal)
}
override operator fun invoke(pref : SharedPreferences) : Float =
pref.getFloat(key, defVal)
override fun put(editor : SharedPreferences.Editor, v : Float) {
if(v == defVal) editor.remove(key) else editor.putFloat(key, v)
@ -97,9 +93,8 @@ class StringPref(
val skipImport : Boolean = false
) : BasePref<String>(key, defVal) {
override operator fun invoke(pref : SharedPreferences) : String {
return pref.getString(key, defVal) ?: defVal
}
override operator fun invoke(pref : SharedPreferences) : String =
pref.getString(key, defVal) ?: defVal
override fun put(editor : SharedPreferences.Editor, v : String) {
if(v == defVal) editor.remove(key) else editor.putString(key, v)
@ -618,10 +613,9 @@ object Pref {
val fpAcctFontSize = FloatPref("acct_font_size", Float.NaN)
val fpNotificationTlFontSize = FloatPref("notification_tl_font_size", Float.NaN)
val fpHeaderTextSize = FloatPref("HeaderTextSize", Float.NaN)
internal const val default_timeline_font_size = 14f
internal const val default_acct_font_size = 12f
internal const val default_notification_tl_font_size = 14f
internal const val default_header_font_size = 14f
}

View File

@ -8,6 +8,7 @@ import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.AccountPicker
import jp.juggler.subwaytooter.dialog.DlgConfirm
import jp.juggler.subwaytooter.removeUser
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.table.UserRelation

View File

@ -1,104 +1,102 @@
package jp.juggler.subwaytooter.action
import androidx.appcompat.app.AlertDialog
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.TootApiResult
import jp.juggler.subwaytooter.api.TootTask
import jp.juggler.subwaytooter.api.TootTaskRunner
import jp.juggler.subwaytooter.api.entity.TootNotification
import jp.juggler.subwaytooter.isNotificationColumn
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.showToast
import jp.juggler.util.toFormRequestBody
import jp.juggler.util.toPost
object Action_Notification {
fun deleteAll(
activity : ActMain, target_account : SavedAccount, bConfirmed : Boolean
) {
if(! bConfirmed) {
AlertDialog.Builder(activity)
.setMessage(R.string.confirm_delete_notification)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
deleteAll(activity, target_account, true)
}
.show()
return
}
TootTaskRunner(activity).run(target_account, object : TootTask {
override suspend fun background(client : TootApiClient) : TootApiResult? {
// 空データを送る
return client.request(
"/api/v1/notifications/clear",
"".toFormRequestBody().toPost()
)
}
override suspend fun handleResult(result : TootApiResult?) {
if(result == null) return // cancelled.
if(result.jsonObject != null) {
// ok. api have return empty object.
for(column in activity.app_state.columnList) {
if(column.isNotificationColumn && column.access_info == target_account) {
column.removeNotifications()
}
}
activity.showToast(false, R.string.delete_succeeded)
} else {
activity.showToast(false, result.error)
}
}
})
}
fun deleteOne(
activity : ActMain, access_info : SavedAccount, notification : TootNotification
) {
TootTaskRunner(activity).run(access_info, object : TootTask {
override suspend fun background(client : TootApiClient) : TootApiResult? {
// https://github.com/tootsuite/mastodon/commit/30f5bcf3e749be9651ed39a07b893f70605f8a39
// 2種類のAPIがあり、片方は除去された
// まず新しいAPIを試す
val result = client.request(
"/api/v1/notifications/${notification.id}/dismiss",
"".toFormRequestBody().toPost()
)
return when(result?.response?.code) {
// 新しいAPIがない場合、古いAPIを試す
422 -> client.request(
"/api/v1/notifications/dismiss",
"id=${notification.id}".toFormRequestBody().toPost()
)
else -> result
}
}
override suspend fun handleResult(result : TootApiResult?) {
if(result == null) return // cancelled.
if(result.jsonObject != null) {
// 成功したら空オブジェクトが返される
for(column in activity.app_state.columnList) {
column.removeNotificationOne(access_info, notification)
}
activity.showToast(true, R.string.delete_succeeded)
} else {
activity.showToast(true, result.error)
}
}
})
}
fun deleteAll(
activity: ActMain, target_account: SavedAccount, bConfirmed: Boolean
) {
if (!bConfirmed) {
AlertDialog.Builder(activity)
.setMessage(R.string.confirm_delete_notification)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ ->
deleteAll(activity, target_account, true)
}
.show()
return
}
TootTaskRunner(activity).run(target_account, object : TootTask {
override suspend fun background(client: TootApiClient): TootApiResult? {
// 空データを送る
return client.request(
"/api/v1/notifications/clear",
"".toFormRequestBody().toPost()
)
}
override suspend fun handleResult(result: TootApiResult?) {
if (result == null) return // cancelled.
if (result.jsonObject != null) {
// ok. api have return empty object.
for (column in activity.app_state.columnList) {
if (column.isNotificationColumn && column.access_info == target_account) {
column.removeNotifications()
}
}
activity.showToast(false, R.string.delete_succeeded)
} else {
activity.showToast(false, result.error)
}
}
})
}
fun deleteOne(
activity: ActMain, access_info: SavedAccount, notification: TootNotification
) {
TootTaskRunner(activity).run(access_info, object : TootTask {
override suspend fun background(client: TootApiClient): TootApiResult? {
// https://github.com/tootsuite/mastodon/commit/30f5bcf3e749be9651ed39a07b893f70605f8a39
// 2種類のAPIがあり、片方は除去された
// まず新しいAPIを試す
val result = client.request(
"/api/v1/notifications/${notification.id}/dismiss",
"".toFormRequestBody().toPost()
)
return when (result?.response?.code) {
// 新しいAPIがない場合、古いAPIを試す
422 -> client.request(
"/api/v1/notifications/dismiss",
"id=${notification.id}".toFormRequestBody().toPost()
)
else -> result
}
}
override suspend fun handleResult(result: TootApiResult?) {
if (result == null) return // cancelled.
if (result.jsonObject != null) {
// 成功したら空オブジェクトが返される
for (column in activity.app_state.columnList) {
column.removeNotificationOne(access_info, notification)
}
activity.showToast(true, R.string.delete_succeeded)
} else {
activity.showToast(true, result.error)
}
}
})
}
}