編集履歴カラムの追加。投稿のヘッダに編集済表記を追加。
This commit is contained in:
parent
8bba82b930
commit
0bbb3728bb
|
@ -3,14 +3,12 @@ package jp.juggler.subwaytooter.action
|
|||
import android.content.Intent
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import jp.juggler.subwaytooter.*
|
||||
import jp.juggler.subwaytooter.actmain.addColumn
|
||||
import jp.juggler.subwaytooter.actmain.reloadAccountSetting
|
||||
import jp.juggler.subwaytooter.actmain.showColumnMatchAccount
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.column.Column
|
||||
import jp.juggler.subwaytooter.column.findStatus
|
||||
import jp.juggler.subwaytooter.column.onScheduleDeleted
|
||||
import jp.juggler.subwaytooter.column.onStatusRemoved
|
||||
import jp.juggler.subwaytooter.column.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||
|
@ -642,3 +640,12 @@ fun ActMain.scheduledPostEdit(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// アカウントを選んでタイムラインカラムを追加
|
||||
fun ActMain.openStatusHistory(
|
||||
pos: Int,
|
||||
accessInfo: SavedAccount,
|
||||
status: TootStatus,
|
||||
) {
|
||||
addColumn(pos, accessInfo, ColumnType.STATUS_HISTORY, status.id, status.json)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.api
|
|||
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.cast
|
||||
import java.util.*
|
||||
|
||||
class DuplicateMap {
|
||||
|
@ -67,4 +68,30 @@ class DuplicateMap {
|
|||
}
|
||||
return listNew
|
||||
}
|
||||
|
||||
private fun isDuplicateByCreatedAt(o: TimelineItem): Boolean {
|
||||
if (o is TootStatus) {
|
||||
val createdAt = EntityId(o.time_created_at.toString())
|
||||
if (createdAt.notDefaultOrConfirming) {
|
||||
if (idSet.contains(createdAt)) return true
|
||||
idSet.add(createdAt)
|
||||
}
|
||||
}
|
||||
//編集履歴以外のカラムではここを通らない
|
||||
return false
|
||||
}
|
||||
|
||||
fun filterDuplicateByCreatedAt(src: Collection<TimelineItem>?): ArrayList<TimelineItem> {
|
||||
val listNew = ArrayList<TimelineItem>()
|
||||
if (src != null) {
|
||||
for (o in src) {
|
||||
if (isDuplicateByCreatedAt(o)) {
|
||||
log.d("filterDuplicateByCreatedAt: filtered. ${o.cast<TootStatus>()?.time_created_at}")
|
||||
continue
|
||||
}
|
||||
listNew.add(o)
|
||||
}
|
||||
}
|
||||
return listNew
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,9 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
var isPromoted = false
|
||||
var isFeatured = false
|
||||
|
||||
// Mastodon 3.5.0
|
||||
var time_edited_at = 0L
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// 以下はentityから取得したデータではなく、アプリ内部で使う
|
||||
|
||||
|
@ -602,6 +605,8 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
this.favourites_count = src.long("favourites_count")
|
||||
this.replies_count = src.long("replies_count")
|
||||
|
||||
this.time_edited_at = parseTime(src.string("edited_at"))
|
||||
|
||||
this.reactionSet = TootReactionSet.parseFedibird(
|
||||
src.jsonArray("emoji_reactions")
|
||||
?: src.jsonObject("pleroma")?.jsonArray("emoji_reactions")
|
||||
|
@ -1267,7 +1272,7 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
// 時刻を解釈してエポック秒(ミリ単位)を返す
|
||||
// 解釈に失敗すると0Lを返す
|
||||
fun parseTime(strTime: String?): Long {
|
||||
if (strTime?.isNotBlank() != true) return 0L
|
||||
if (strTime.isNullOrBlank()) return 0L
|
||||
|
||||
// last_status_at などでは YYYY-MM-DD になることがある
|
||||
reDate.find(strTime)?.groupValues?.let { gv ->
|
||||
|
@ -1552,5 +1557,23 @@ class TootStatus(parser: TootParser, src: JsonObject) : TimelineItem() {
|
|||
|
||||
return null
|
||||
}
|
||||
|
||||
private val supplyEditHistoryKeys = arrayOf(
|
||||
"id",
|
||||
"uri",
|
||||
"url",
|
||||
"visibility",
|
||||
)
|
||||
|
||||
// 編集履歴のデータはTootStatusとしては不足があるので、srcを元に補う
|
||||
fun supplyEditHistory(array: JsonArray?, src: JsonObject?) {
|
||||
src ?: return
|
||||
array?.objectList()?.forEach {
|
||||
for (key in supplyEditHistoryKeys) {
|
||||
if (it.containsKey(key)) continue
|
||||
it[key] = src[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,6 +160,7 @@ class Column(
|
|||
internal var profileTab = ProfileTab.Status
|
||||
|
||||
internal var statusId: EntityId? = null
|
||||
internal var originalStatus: JsonObject? = null
|
||||
|
||||
// プロフカラムではアカウントのID。リストカラムではリストのID
|
||||
internal var profileId: EntityId? = null
|
||||
|
|
|
@ -242,6 +242,7 @@ fun Column.onMuteUpdated() {
|
|||
}
|
||||
|
||||
fun Column.replaceStatus(statusId: EntityId, statusJson: JsonObject) {
|
||||
if (type == ColumnType.STATUS_HISTORY) return
|
||||
|
||||
fun createStatus() =
|
||||
TootParser(context, accessInfo).status(statusJson)
|
||||
|
|
|
@ -56,6 +56,7 @@ object ColumnEncoder {
|
|||
private const val KEY_PROFILE_ID = "profile_id"
|
||||
private const val KEY_PROFILE_TAB = "tab"
|
||||
private const val KEY_STATUS_ID = "status_id"
|
||||
private const val KEY_ORIGINAL_STATUS = "original_status"
|
||||
|
||||
private const val KEY_HASHTAG = "hashtag"
|
||||
private const val KEY_HASHTAG_ANY = "hashtag_any"
|
||||
|
@ -170,6 +171,11 @@ object ColumnEncoder {
|
|||
->
|
||||
dst[KEY_STATUS_ID] = statusId.toString()
|
||||
|
||||
ColumnType.STATUS_HISTORY -> {
|
||||
dst[KEY_STATUS_ID] = statusId.toString()
|
||||
dst[KEY_ORIGINAL_STATUS] = originalStatus
|
||||
}
|
||||
|
||||
ColumnType.FEDERATED_AROUND -> {
|
||||
dst[KEY_STATUS_ID] = statusId.toString()
|
||||
dst[KEY_REMOTE_ONLY] = remoteOnly
|
||||
|
@ -299,6 +305,11 @@ object ColumnEncoder {
|
|||
ColumnType.ACCOUNT_AROUND,
|
||||
-> statusId = EntityId.mayNull(src.string(KEY_STATUS_ID))
|
||||
|
||||
ColumnType.STATUS_HISTORY -> {
|
||||
statusId = EntityId.mayNull(src.string(KEY_STATUS_ID))
|
||||
originalStatus = src.jsonObject(KEY_ORIGINAL_STATUS)
|
||||
}
|
||||
|
||||
ColumnType.FEDERATED_AROUND,
|
||||
-> {
|
||||
statusId = EntityId.mayNull(src.string(KEY_STATUS_ID))
|
||||
|
|
|
@ -36,6 +36,7 @@ fun Column.canReloadWhenRefreshTop(): Boolean = when (type) {
|
|||
ColumnType.TREND_TAG,
|
||||
ColumnType.FOLLOW_SUGGESTION,
|
||||
ColumnType.PROFILE_DIRECTORY,
|
||||
ColumnType.STATUS_HISTORY,
|
||||
-> true
|
||||
|
||||
ColumnType.LIST_MEMBER,
|
||||
|
@ -56,7 +57,7 @@ fun Column.canRefreshTopBySwipe(): Boolean =
|
|||
else -> true
|
||||
}
|
||||
|
||||
// カラム操作的にリフレッシュを許容するかどうか
|
||||
// カラム操作的に下端リフレッシュを許容するかどうか
|
||||
fun Column.canRefreshBottomBySwipe(): Boolean = when (type) {
|
||||
ColumnType.LIST_LIST,
|
||||
ColumnType.CONVERSATION,
|
||||
|
@ -65,6 +66,7 @@ fun Column.canRefreshBottomBySwipe(): Boolean = when (type) {
|
|||
ColumnType.SEARCH,
|
||||
ColumnType.TREND_TAG,
|
||||
ColumnType.FOLLOW_SUGGESTION,
|
||||
ColumnType.STATUS_HISTORY,
|
||||
-> false
|
||||
|
||||
ColumnType.FOLLOW_REQUESTS -> isMisskey
|
||||
|
|
|
@ -44,20 +44,26 @@ fun Column.getFilterContext() = when (type) {
|
|||
|
||||
ColumnType.PROFILE -> TootFilter.CONTEXT_PROFILE
|
||||
|
||||
ColumnType.STATUS_HISTORY -> TootFilter.CONTEXT_NONE
|
||||
|
||||
else -> TootFilter.CONTEXT_PUBLIC
|
||||
// ColumnType.MISSKEY_HYBRID や ColumnType.MISSKEY_ANTENNA_TL はHOMEでもPUBLICでもある…
|
||||
// Misskeyだし関係ないが、NONEにするとアプリ内で完結するフィルタも働かなくなる
|
||||
}
|
||||
|
||||
// カラム設定に正規表現フィルタを含めるなら真
|
||||
fun Column.canStatusFilter(): Boolean {
|
||||
if (getFilterContext() != TootFilter.CONTEXT_NONE) return true
|
||||
|
||||
return when (type) {
|
||||
ColumnType.SEARCH_MSP, ColumnType.SEARCH_TS, ColumnType.SEARCH_NOTESTOCK -> true
|
||||
else -> false
|
||||
fun Column.canStatusFilter() =
|
||||
when (type) {
|
||||
ColumnType.SEARCH_MSP,
|
||||
ColumnType.SEARCH_TS,
|
||||
ColumnType.SEARCH_NOTESTOCK,
|
||||
ColumnType.STATUS_HISTORY,
|
||||
-> true
|
||||
else -> when {
|
||||
getFilterContext() == TootFilter.CONTEXT_NONE -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// カラム設定に「すべての画像を隠す」ボタンを含めるなら真
|
||||
fun Column.canNSFWDefault(): Boolean = canStatusFilter()
|
||||
|
|
|
@ -4,6 +4,7 @@ import jp.juggler.subwaytooter.api.entity.Acct
|
|||
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.JsonObject
|
||||
import jp.juggler.util.LogCategory
|
||||
|
||||
private val log = LogCategory("ColumnSpec")
|
||||
|
@ -34,6 +35,12 @@ object ColumnSpec {
|
|||
else -> error("getParamString [$idx] bad type. $o")
|
||||
}
|
||||
|
||||
private fun getParamJsonObject(params: Array<out Any>, idx: Int): JsonObject =
|
||||
when (val o = params[idx]) {
|
||||
is JsonObject -> o
|
||||
else -> error("getParamJsonObject [$idx] bad type. $o")
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private inline fun <reified T> getParamAtNullable(params: Array<out Any>, idx: Int): T? {
|
||||
if (idx >= params.size) return null
|
||||
|
@ -53,6 +60,11 @@ object ColumnSpec {
|
|||
->
|
||||
statusId = getParamEntityId(params, 0)
|
||||
|
||||
ColumnType.STATUS_HISTORY -> {
|
||||
statusId = getParamEntityId(params, 0)
|
||||
originalStatus = getParamJsonObject(params, 1)
|
||||
}
|
||||
|
||||
ColumnType.PROFILE, ColumnType.LIST_TL, ColumnType.LIST_MEMBER,
|
||||
ColumnType.MISSKEY_ANTENNA_TL,
|
||||
->
|
||||
|
@ -121,19 +133,20 @@ object ColumnSpec {
|
|||
ColumnType.LOCAL_AROUND,
|
||||
ColumnType.FEDERATED_AROUND,
|
||||
ColumnType.ACCOUNT_AROUND,
|
||||
ColumnType.STATUS_HISTORY,
|
||||
->
|
||||
column.statusId == getParamEntityId(params, 0)
|
||||
|
||||
ColumnType.HASHTAG -> {
|
||||
(getParamString(params, 0) == column.hashtag) &&
|
||||
((getParamAtNullable<String>(params, 1) ?: "") == column.hashtagAny) &&
|
||||
((getParamAtNullable<String>(params, 2) ?: "") == column.hashtagAll) &&
|
||||
((getParamAtNullable<String>(params, 3) ?: "") == column.hashtagNone)
|
||||
((getParamAtNullable<String>(params, 1) ?: "") == column.hashtagAny) &&
|
||||
((getParamAtNullable<String>(params, 2) ?: "") == column.hashtagAll) &&
|
||||
((getParamAtNullable<String>(params, 3) ?: "") == column.hashtagNone)
|
||||
}
|
||||
|
||||
ColumnType.HASHTAG_FROM_ACCT -> {
|
||||
(getParamString(params, 0) == column.hashtag) &&
|
||||
((getParamAtNullable<String>(params, 1) ?: "") == column.hashtagAcct)
|
||||
((getParamAtNullable<String>(params, 1) ?: "") == column.hashtagAcct)
|
||||
}
|
||||
|
||||
ColumnType.NOTIFICATION_FROM_ACCT -> {
|
||||
|
@ -142,7 +155,7 @@ object ColumnSpec {
|
|||
|
||||
ColumnType.SEARCH ->
|
||||
getParamString(params, 0) == column.searchQuery &&
|
||||
getParamAtNullable<Boolean>(params, 1) == column.searchResolve
|
||||
getParamAtNullable<Boolean>(params, 1) == column.searchResolve
|
||||
|
||||
ColumnType.SEARCH_MSP,
|
||||
ColumnType.SEARCH_TS,
|
||||
|
@ -155,8 +168,8 @@ object ColumnSpec {
|
|||
|
||||
ColumnType.PROFILE_DIRECTORY ->
|
||||
getParamString(params, 0) == column.instanceUri &&
|
||||
getParamAtNullable<String>(params, 1) == column.searchQuery &&
|
||||
getParamAtNullable<Boolean>(params, 2) == column.searchResolve
|
||||
getParamAtNullable<String>(params, 1) == column.searchQuery &&
|
||||
getParamAtNullable<Boolean>(params, 2) == column.searchResolve
|
||||
|
||||
ColumnType.DOMAIN_TIMELINE ->
|
||||
getParamString(params, 0) == column.instanceUri
|
||||
|
|
|
@ -104,6 +104,9 @@ class ColumnTask_Loading(
|
|||
// 検索カラムはIDによる重複排除が不可能
|
||||
ColumnType.SEARCH -> listTmp
|
||||
|
||||
// 編集履歴は投稿日時で重複排除する
|
||||
ColumnType.STATUS_HISTORY -> column.duplicateMap.filterDuplicateByCreatedAt(listTmp)
|
||||
|
||||
// 他のカラムは重複排除してから追加
|
||||
else -> column.duplicateMap.filterDuplicate(listTmp)
|
||||
}
|
||||
|
@ -847,6 +850,19 @@ class ColumnTask_Loading(
|
|||
return result
|
||||
}
|
||||
|
||||
suspend fun getEditHistory(client: TootApiClient): TootApiResult? {
|
||||
// ページングなし
|
||||
val result = client.request("/api/v1/statuses/${column.statusId}/history")
|
||||
|
||||
// TootStatusとしては不足している情報があるのを補う
|
||||
TootStatus.supplyEditHistory(result?.jsonArray, column.originalStatus)
|
||||
|
||||
val src = parser.statusList(result?.jsonArray).reversed()
|
||||
listTmp = addAll(listTmp, src)
|
||||
column.saveRange(bBottom = true, bTop = true, result = result, list = src)
|
||||
return result
|
||||
}
|
||||
|
||||
suspend fun getListList(
|
||||
client: TootApiClient,
|
||||
pathBase: String,
|
||||
|
|
|
@ -92,7 +92,12 @@ class ColumnTask_Refresh(
|
|||
return
|
||||
}
|
||||
|
||||
val listNew = column.duplicateMap.filterDuplicate(listTmp)
|
||||
val listNew = when (column.type) {
|
||||
// 編集履歴は投稿日時で重複排除する
|
||||
ColumnType.STATUS_HISTORY -> column.duplicateMap.filterDuplicateByCreatedAt(listTmp)
|
||||
|
||||
else -> column.duplicateMap.filterDuplicate(listTmp)
|
||||
}
|
||||
if (listNew.isEmpty()) {
|
||||
column.fireShowContent(
|
||||
reason = "refresh list_new is empty",
|
||||
|
@ -1153,4 +1158,17 @@ class ColumnTask_Refresh(
|
|||
column.saveRange(bBottom, !bBottom, result, src)
|
||||
return result
|
||||
}
|
||||
|
||||
suspend fun getEditHistory(client: TootApiClient): TootApiResult? {
|
||||
// ページングなし
|
||||
val result = client.request("/api/v1/statuses/${column.statusId}/history")
|
||||
|
||||
// TootStatusとしては不足している情報があるのを補う
|
||||
TootStatus.supplyEditHistory(result?.jsonArray, column.originalStatus)
|
||||
|
||||
val src = parser.statusList(result?.jsonArray).reversed()
|
||||
listTmp = addAll(listTmp, src)
|
||||
column.saveRange(bBottom, !bBottom, result, src)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -663,7 +663,11 @@ enum class ColumnType(
|
|||
},
|
||||
|
||||
loading = { client -> getPublicTlAroundTime(client, column.makePublicFederateUrl()) },
|
||||
refresh = { client -> getStatusList(client, column.makePublicFederateUrl(), useMinId = true) },
|
||||
refresh = { client ->
|
||||
getStatusList(client,
|
||||
column.makePublicFederateUrl(),
|
||||
useMinId = true)
|
||||
},
|
||||
|
||||
canStreamingMastodon = streamingTypeNo,
|
||||
canStreamingMisskey = streamingTypeNo,
|
||||
|
@ -1965,6 +1969,24 @@ enum class ColumnType(
|
|||
// Misskey10 にアンテナはない
|
||||
),
|
||||
|
||||
STATUS_HISTORY(
|
||||
43,
|
||||
iconId = { R.drawable.ic_history },
|
||||
name1 = { it.getString(R.string.edit_history) },
|
||||
bAllowPseudo = true,
|
||||
bAllowMisskey = false,
|
||||
|
||||
loading = { client ->
|
||||
getEditHistory(client)
|
||||
},
|
||||
refresh = { client ->
|
||||
getEditHistory(client)
|
||||
},
|
||||
|
||||
canStreamingMastodon = streamingTypeNo,
|
||||
canStreamingMisskey = streamingTypeNo,
|
||||
),
|
||||
|
||||
;
|
||||
|
||||
init {
|
||||
|
@ -1985,7 +2007,7 @@ enum class ColumnType(
|
|||
min = min(min, id)
|
||||
max = max(max, id)
|
||||
}
|
||||
log.d("dump: ColumnType range=$min..$max")
|
||||
log.i("dump: ColumnType range=$min..$max")
|
||||
}
|
||||
|
||||
fun parse(id: Int) = Column.typeMap[id] ?: HOME
|
||||
|
@ -2015,7 +2037,11 @@ fun Column.streamKeyHashtagTl() =
|
|||
"hashtag"
|
||||
.appendIf(":local", instanceLocal)
|
||||
|
||||
private fun unmatchMastodonStream(stream: JsonArray, name: String, expectArg: String? = null): Boolean {
|
||||
private fun unmatchMastodonStream(
|
||||
stream: JsonArray,
|
||||
name: String,
|
||||
expectArg: String? = null,
|
||||
): Boolean {
|
||||
|
||||
val key = stream.string(0)
|
||||
|
||||
|
|
|
@ -88,71 +88,11 @@ internal class DlgContextMenu(
|
|||
dialog.setCancelable(true)
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
|
||||
arrayOf(
|
||||
views.btnAccountWebPage,
|
||||
views.btnAroundAccountTL,
|
||||
views.btnAroundFTL,
|
||||
views.btnAroundLTL,
|
||||
views.btnAvatarImage,
|
||||
views.btnBlock,
|
||||
views.btnBookmarkAnotherAccount,
|
||||
views.btnBoostAnotherAccount,
|
||||
views.btnBoostedBy,
|
||||
views.btnBoostWithVisibility,
|
||||
views.btnConversationAnotherAccount,
|
||||
views.btnConversationMute,
|
||||
views.btnCopyAccountId,
|
||||
views.btnDelete,
|
||||
views.btnDeleteSuggestion,
|
||||
views.btnDomainBlock,
|
||||
views.btnDomainTimeline,
|
||||
views.btnEndorse,
|
||||
views.btnFavouriteAnotherAccount,
|
||||
views.btnFavouritedBy,
|
||||
views.btnFollow,
|
||||
views.btnFollowFromAnotherAccount,
|
||||
views.btnFollowRequestNG,
|
||||
views.btnFollowRequestOK,
|
||||
views.btnHideBoost,
|
||||
views.btnHideFavourite,
|
||||
views.btnInstanceInformation,
|
||||
views.btnListMemberAddRemove,
|
||||
views.btnMute,
|
||||
views.btnMuteApp,
|
||||
views.btnNotificationDelete,
|
||||
views.btnNotificationFrom,
|
||||
views.btnOpenAccountInAdminWebUi,
|
||||
views.btnOpenInstanceInAdminWebUi,
|
||||
views.btnOpenProfileFromAnotherAccount,
|
||||
views.btnOpenTimeline,
|
||||
views.btnProfile,
|
||||
views.btnProfileDirectory,
|
||||
views.btnProfilePin,
|
||||
views.btnProfileUnpin,
|
||||
views.btnQuoteAnotherAccount,
|
||||
views.btnQuoteTootBT,
|
||||
views.btnReactionAnotherAccount,
|
||||
views.btnRedraft,
|
||||
views.btnStatusEdit,
|
||||
views.btnReplyAnotherAccount,
|
||||
views.btnReportStatus,
|
||||
views.btnReportUser,
|
||||
views.btnSendMessage,
|
||||
views.btnSendMessageFromAnotherAccount,
|
||||
views.btnShowBoost,
|
||||
views.btnShowFavourite,
|
||||
views.btnStatusNotification,
|
||||
views.btnStatusWebPage,
|
||||
views.btnText,
|
||||
|
||||
views.btnQuoteUrlStatus,
|
||||
views.btnTranslate,
|
||||
views.btnQuoteUrlAccount,
|
||||
views.btnShareUrlStatus,
|
||||
views.btnShareUrlAccount,
|
||||
views.btnQuoteName
|
||||
|
||||
).forEach { it.setOnClickListener(this) }
|
||||
views.root.scan { v ->
|
||||
when (v) {
|
||||
is Button -> v.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
arrayOf(
|
||||
views.btnBlock,
|
||||
|
@ -219,6 +159,11 @@ internal class DlgContextMenu(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
views.btnStatusHistory.vg(status.time_edited_at > 0L && columnType != ColumnType.STATUS_HISTORY)
|
||||
?.text = activity.getString(R.string.edit_history) + "\n" +
|
||||
TootStatus.formatTime(activity, status.time_edited_at, bAllowRelative = false)
|
||||
|
||||
views.llLinks.vg(views.llLinks.childCount > 1)
|
||||
|
||||
views.btnGroupStatusByMe.vg(statusByMe)
|
||||
|
@ -372,8 +317,6 @@ internal class DlgContextMenu(
|
|||
}
|
||||
}
|
||||
|
||||
views.btnAccountText.setOnClickListener(this)
|
||||
|
||||
if (accessInfo.isPseudo) {
|
||||
views.btnProfile.visibility = View.GONE
|
||||
views.btnSendMessage.visibility = View.GONE
|
||||
|
@ -399,10 +342,6 @@ internal class DlgContextMenu(
|
|||
views.btnSendMessageFromAnotherAccount.visibility = View.GONE
|
||||
}
|
||||
|
||||
views.btnNickname.setOnClickListener(this)
|
||||
views.btnCancel.setOnClickListener(this)
|
||||
views.btnAccountQrCode.setOnClickListener(this)
|
||||
|
||||
if (accessInfo.isPseudo ||
|
||||
who == null ||
|
||||
!relation.getFollowing(who) ||
|
||||
|
@ -641,6 +580,7 @@ internal class DlgContextMenu(
|
|||
R.id.btnConversationMute -> conversationMute(accessInfo, status)
|
||||
R.id.btnProfilePin -> statusPin(accessInfo, status, true)
|
||||
R.id.btnProfileUnpin -> statusPin(accessInfo, status, false)
|
||||
R.id.btnStatusHistory -> openStatusHistory(pos, accessInfo, status)
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -2,10 +2,12 @@ package jp.juggler.subwaytooter.itemviewholder
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.os.SystemClock
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
|
@ -602,6 +604,14 @@ fun ItemViewHolder.showStatusTime(
|
|||
if (sb.isNotEmpty()) sb.append(' ')
|
||||
sb.append(activity.getString(R.string.featured))
|
||||
}
|
||||
|
||||
if (status.time_edited_at > 0L) {
|
||||
if (sb.isNotEmpty()) sb.append(' ')
|
||||
val start = sb.length
|
||||
sb.append(activity.getString(R.string.edited))
|
||||
val end = sb.length
|
||||
sb.setSpan(StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
} else {
|
||||
reblogVisibility?.takeIf { it != TootVisibility.Unknown }?.let { visibility ->
|
||||
val visIconId = Styler.getVisibilityIconId(accessInfo.isMisskey, visibility)
|
||||
|
@ -626,12 +636,18 @@ fun ItemViewHolder.showStatusTime(
|
|||
time != null -> TootStatus.formatTime(
|
||||
activity,
|
||||
time,
|
||||
column.type != ColumnType.CONVERSATION
|
||||
when (column.type) {
|
||||
ColumnType.CONVERSATION, ColumnType.STATUS_HISTORY -> false
|
||||
else -> true
|
||||
}
|
||||
)
|
||||
status != null -> TootStatus.formatTime(
|
||||
activity,
|
||||
status.time_created_at,
|
||||
column.type != ColumnType.CONVERSATION
|
||||
when (column.type) {
|
||||
ColumnType.CONVERSATION, ColumnType.STATUS_HISTORY -> false
|
||||
else -> true
|
||||
}
|
||||
)
|
||||
else -> "?"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
||||
</vector>
|
|
@ -50,6 +50,21 @@
|
|||
android:textColor="?attr/colorTimeSmall"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnStatusHistory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/btn_bg_transparent_round6dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="32dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="@string/edit_history"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnStatusWebPage"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1127,4 +1127,6 @@
|
|||
<string name="always">常に</string>
|
||||
<string name="auto">自動</string>
|
||||
<string name="status_edit">編集(Mastodon 3.5.0+)</string>
|
||||
<string name="edited">編集済</string>
|
||||
<string name="edit_history">編集履歴</string>
|
||||
</resources>
|
||||
|
|
|
@ -1138,4 +1138,6 @@
|
|||
<string name="always">Always</string>
|
||||
<string name="auto">Auto</string>
|
||||
<string name="status_edit">Edit (Mastodon 3.5.0+)</string>
|
||||
<string name="edited">edited</string>
|
||||
<string name="edit_history">Edit history</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue