(Mastodon開発版)ブックマーク機能に対応。

This commit is contained in:
tateisu 2019-11-14 09:51:17 +09:00
parent 1f67e44406
commit fad9004789
13 changed files with 334 additions and 27 deletions

View File

@ -110,6 +110,8 @@
<w>tootsearch</w>
<w>transcoder</w>
<w>unarist</w>
<w>unbookmark</w>
<w>unbookmarked</w>
<w>unboost</w>
<w>unboosted</w>
<w>unendorsemed</w>

View File

@ -188,13 +188,20 @@ class ActMain : AppCompatActivity()
val cancel_follow_request_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.follow_request_cancelled)
}
val favourite_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.favourite_succeeded)
}
val unfavourite_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.unfavourite_succeeded)
}
val bookmark_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.bookmark_succeeded)
}
val unbookmark_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.unbookmark_succeeded)
}
val boost_complete_callback : EmptyCallback = {
showToast(this@ActMain, false, R.string.boost_succeeded)

View File

@ -114,6 +114,7 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
val column_list = ArrayList<Column>()
private val map_busy_fav = HashSet<String>()
private val map_busy_bookmark = HashSet<String>()
private val map_busy_boost = HashSet<String>()
internal var attachment_list : ArrayList<PostAttachment>? = null
@ -329,6 +330,23 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
map_busy_fav.remove(key)
}
fun isBusyBookmark(account : SavedAccount, status : TootStatus) : Boolean {
val key = account.acct + ":" + status.busyKey
return map_busy_bookmark.contains(key)
}
fun setBusyBookmark(account : SavedAccount, status : TootStatus) {
val key = account.acct + ":" + status.busyKey
map_busy_bookmark.add(key)
}
fun resetBusyBookmark(account : SavedAccount, status : TootStatus) {
val key = account.acct + ":" + status.busyKey
map_busy_bookmark.remove(key)
}
fun isBusyBoost(account : SavedAccount, status : TootStatus) : Boolean {
val key = account.acct + ":" + status.busyKey
return map_busy_boost.contains(key)

View File

@ -87,6 +87,7 @@ class Column(
internal const val PATH_LOCAL = "/api/v1/timelines/public?limit=$READ_LIMIT&local=true"
internal const val PATH_TL_FEDERATE = "/api/v1/timelines/public?limit=$READ_LIMIT"
internal const val PATH_FAVOURITES = "/api/v1/favourites?limit=$READ_LIMIT"
internal const val PATH_BOOKMARKS = "/api/v1/bookmarks?limit=$READ_LIMIT"
internal const val PATH_LIST_TL = "/api/v1/timelines/list/%s?limit=$READ_LIMIT"

View File

@ -565,6 +565,37 @@ enum class ColumnType(
}
),
BOOKMARKS(37,
iconId = { R.drawable.ic_bookmark },
name1 = { it.getString(R.string.bookmarks) },
bAllowPseudo = false,
loading = { client ->
if(isMisskey) {
TootApiResult("Misskey has no bookmarks feature.")
} else {
getStatusList(client, Column.PATH_BOOKMARKS)
}
},
refresh = { client ->
if(isMisskey) {
TootApiResult("Misskey has no bookmarks feature.")
} else {
getStatusList(client, Column.PATH_BOOKMARKS)
}
},
gap = { client ->
if(isMisskey) {
TootApiResult("Misskey has no bookmarks feature.")
} else {
getStatusList(client, Column.PATH_BOOKMARKS)
}
}
),
NOTIFICATIONS(
7,
iconId = { R.drawable.ic_announcement },

View File

@ -102,6 +102,8 @@ internal class DlgContextMenu(
val btnText : View = viewRoot.findViewById(R.id.btnText)
val btnFavouriteAnotherAccount : View =
viewRoot.findViewById(R.id.btnFavouriteAnotherAccount)
val btnBookmarkAnotherAccount : View =
viewRoot.findViewById(R.id.btnBookmarkAnotherAccount)
val btnBoostAnotherAccount : View = viewRoot.findViewById(R.id.btnBoostAnotherAccount)
val btnReactionAnotherAccount : View = viewRoot.findViewById(R.id.btnReactionAnotherAccount)
val btnReplyAnotherAccount : View = viewRoot.findViewById(R.id.btnReplyAnotherAccount)
@ -176,6 +178,7 @@ internal class DlgContextMenu(
btnStatusWebPage,
btnText,
btnFavouriteAnotherAccount,
btnBookmarkAnotherAccount,
btnBoostAnotherAccount,
btnReactionAnotherAccount,
btnReplyAnotherAccount,
@ -494,8 +497,6 @@ internal class DlgContextMenu(
}
}
btnListMemberAddRemove.visibility = View.VISIBLE
updateGroup(btnCrossAccountActionsForStatus, llCrossAccountActionsForStatus)
@ -534,10 +535,11 @@ internal class DlgContextMenu(
}
when {
Pref.bpAlwaysExpandContextMenuItems(activity.pref) ->{
Pref.bpAlwaysExpandContextMenuItems(activity.pref) -> {
vg(group, true)
btn.background = null
}
toggle -> vg(group, group.visibility != View.VISIBLE)
else -> btn.setOnClickListener(this)
}
@ -983,6 +985,12 @@ internal class DlgContextMenu(
status
)
R.id.btnBookmarkAnotherAccount -> Action_Toot.bookmarkFromAnotherAccount(
activity,
access_info,
status
)
R.id.btnBoostAnotherAccount -> Action_Toot.boostFromAnotherAccount(
activity,
access_info,

View File

@ -104,6 +104,10 @@ class SideMenuAdapter(
Action_Account.timeline(this, defaultInsertPosition, ColumnType.FAVOURITES)
},
Item(icon = R.drawable.ic_bookmark, title = R.string.bookmarks) {
Action_Account.timeline(this, defaultInsertPosition, ColumnType.BOOKMARKS)
},
Item(icon = R.drawable.ic_account_box, title = R.string.profile) {
Action_Account.timeline(this, defaultInsertPosition, ColumnType.PROFILE)
},

View File

@ -47,6 +47,7 @@ internal class StatusButtons(
private val btnReply = holder.btnReply
private val btnBoost = holder.btnBoost
private val btnFavourite = holder.btnFavourite
private val btnBookmark = holder.btnBookmark
private val llFollow2 = holder.llFollow2
private val btnFollow2 = holder.btnFollow2
private val ivFollowedBy2 = holder.ivFollowedBy2
@ -68,6 +69,8 @@ internal class StatusButtons(
btnBoost.setOnLongClickListener(this)
btnFavourite.setOnClickListener(this)
btnFavourite.setOnLongClickListener(this)
btnBookmark.setOnClickListener(this)
btnBookmark.setOnLongClickListener(this)
btnFollow2.setOnClickListener(this)
btnFollow2.setOnLongClickListener(this)
btnTranslate.setOnClickListener(this)
@ -90,11 +93,6 @@ internal class StatusButtons(
this.status = status
this.notification = notification
val fav_icon_drawable = when {
access_info.isNicoru(status.account) -> R.drawable.ic_nicoru
else -> R.drawable.ic_star
}
val replies_count = status.replies_count
setIconDrawableId(
@ -175,6 +173,11 @@ internal class StatusButtons(
)
}
// お気に入りボタン
val fav_icon_drawable = when {
access_info.isNicoru(status.account) -> R.drawable.ic_nicoru
else -> R.drawable.ic_star
}
when {
activity.app_state.isBusyFav(access_info, status) -> setButton(
btnFavourite,
@ -195,6 +198,25 @@ internal class StatusButtons(
)
}
// ブックマークボタン
when {
activity.app_state.isBusyBookmark(access_info, status) -> setButton(
btnBookmark,
false,
color_normal,
R.drawable.ic_refresh,
activity.getString(R.string.bookmark)
)
else -> setButton(
btnBookmark,
true,
if(status.bookmarked) color_accent else color_normal,
R.drawable.ic_bookmark,
activity.getString(R.string.bookmark)
)
}
val account = status.account
this.relation = if(! Pref.bpShowFollowButtonInButtonBar(activity.pref)) {
@ -343,24 +365,24 @@ internal class StatusButtons(
b.isEnabled = enabled
}
// private fun setButton(
// b : ImageButton,
// enabled : Boolean,
// color : Int,
// drawableId : Int,
// contentDescription : String
// ) {
// val alpha = Styler.boost_alpha
// val d = createColoredDrawable(
// activity,
// drawableId,
// color,
// alpha
// )
// b.setImageDrawable(d)
// b.contentDescription = contentDescription
// b.isEnabled = enabled
// }
private fun setButton(
b : ImageButton,
enabled : Boolean,
color : Int,
drawableId : Int,
contentDescription : String
) {
val alpha = Styler.boost_alpha
val d = createColoredDrawable(
activity,
drawableId,
color,
alpha
)
b.setImageDrawable(d)
b.contentDescription = contentDescription
b.isEnabled = enabled
}
override fun onClick(v : View) {
@ -452,6 +474,30 @@ internal class StatusButtons(
}
}
btnBookmark -> {
if(access_info.isPseudo) {
Action_Toot.bookmarkFromAnotherAccount(activity, access_info, status)
} else {
// トグル動作
val bSet = ! status.bookmarked
Action_Toot.bookmark(
activity,
access_info,
status,
NOT_CROSS_ACCOUNT,
when {
! bSimpleList -> null
// 簡略表示なら結果をトースト表示
bSet -> activity.bookmark_complete_callback
else -> activity.unbookmark_complete_callback
},
bSet = bSet
)
}
}
btnFollow2 -> {
val accountRef = status.accountRef
val account = accountRef.get()
@ -568,6 +614,10 @@ internal class StatusButtons(
activity, access_info, status
)
btnBookmark -> Action_Toot.bookmarkFromAnotherAccount(
activity, access_info, status
)
btnReply -> Action_Toot.replyFromAnotherAccount(
activity, access_info, status
)
@ -616,6 +666,7 @@ class StatusButtonsViewHolder(
lateinit var btnReply : CountImageButton
lateinit var btnBoost : CountImageButton
lateinit var btnFavourite : CountImageButton
lateinit var btnBookmark : ImageButton
lateinit var llFollow2 : View
lateinit var btnFollow2 : ImageButton
lateinit var ivFollowedBy2 : ImageView
@ -690,6 +741,19 @@ class StatusButtonsViewHolder(
startMargin = marginBetween
}
btnBookmark = imageButton {
background = ContextCompat.getDrawable(
context,
R.drawable.btn_bg_transparent
)
setPadding(paddingH, paddingV, paddingH, paddingV)
scaleType = ImageView.ScaleType.FIT_CENTER
minimumWidth = buttonHeight
}.lparams(wrapContent, buttonHeight) {
startMargin = marginBetween
}
llFollow2 = frameLayout {
lparams(buttonHeight, buttonHeight) {
startMargin = marginBetween

View File

@ -222,6 +222,140 @@ object Action_Toot {
activity.showColumnMatchAccount(access_info)
}
// アカウントを選んでお気に入り
fun bookmarkFromAnotherAccount(
activity : ActMain,
timeline_account : SavedAccount,
status : TootStatus?
) {
if(status == null) return
val who_host = timeline_account.host
AccountPicker.pick(
activity,
bAllowPseudo = false,
bAuto = false,
message = activity.getString(R.string.account_picker_bookmark),
accountListArg = makeAccountListNonPseudo(activity, who_host)
) { action_account ->
bookmark(
activity,
action_account,
status,
calcCrossAccountMode(timeline_account, action_account),
callback = activity.bookmark_complete_callback
)
}
}
// お気に入りの非同期処理
fun bookmark(
activity : ActMain,
access_info : SavedAccount,
arg_status : TootStatus,
nCrossAccountMode : Int,
callback : EmptyCallback?,
bSet : Boolean = true,
bConfirmed : Boolean = false
) {
if(App1.getAppState(activity).isBusyFav(access_info, arg_status)) {
showToast(activity, false, R.string.wait_previous_operation)
return
}
if( access_info.isMisskey ){
showToast(activity, false, R.string.misskey_account_not_supported)
return
}
// 必要なら確認を出す
// ブックマークは解除する時だけ確認する
if(! bConfirmed && !bSet ) {
DlgConfirm.openSimple(
activity,
activity.getString(
R.string.confirm_unfavourite_from,
AcctColor.getNickname(access_info.acct)
)
){
bookmark(
activity,
access_info,
arg_status,
nCrossAccountMode,
callback,
bSet = bSet,
bConfirmed = true
)
}
return
}
//
App1.getAppState(activity).setBusyBookmark(access_info, arg_status)
//
TootTaskRunner(activity, TootTaskRunner.PROGRESS_NONE).run(access_info, object : TootTask {
var new_status : TootStatus? = null
override fun background(client : TootApiClient) : TootApiResult? {
val target_status = if(nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE) {
val (result, status) = client.syncStatus(access_info, arg_status)
status ?: return result
if(status.bookmarked) {
return TootApiResult(activity.getString(R.string.already_bookmarked))
}
status
} else {
arg_status
}
return client.request(
"/api/v1/statuses/${target_status.id}/${if(bSet) "bookmark" else "unbookmark"}",
"".toFormRequestBody().toPost()
)?.also { result ->
new_status = TootParser(activity, access_info).status(result.jsonObject)
}
}
override fun handleResult(result : TootApiResult?) {
App1.getAppState(activity).resetBusyBookmark(access_info, arg_status)
val new_status = this.new_status
when {
result == null -> {
} // cancelled.
new_status != null -> {
for(column in App1.getAppState(activity).column_list) {
column.findStatus(access_info.host, new_status.id) { account, status ->
// 同アカウントならブックマーク状態を伝播する
if(access_info.acct == account.acct) {
status.bookmarked = new_status.bookmarked
}
true
}
}
if(callback != null) callback()
}
else -> showToast(activity, true, result.error)
}
// 結果に関わらず、更新中状態から復帰させる
activity.showColumnMatchAccount(access_info)
}
})
// ファボ表示を更新中にする
activity.showColumnMatchAccount(access_info)
}
fun boostFromAnotherAccount(
activity : ActMain,
timeline_account : SavedAccount,

View File

@ -79,6 +79,10 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
// Whether the authenticated user has favourited the status
var favourited : Boolean = false // アプリから変更する
// Whether the authenticated user has bookmarked the status
var bookmarked : Boolean = false // アプリから変更する
// Whether the authenticated user has muted the conversation this status from
var muted : Boolean = false // アプリから変更する
@ -401,6 +405,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
this.reblogged = src.optBoolean("reblogged")
this.favourited = src.optBoolean("favourited")
this.bookmarked = src.optBoolean("bookmarked")
this.time_created_at = parseTime(this.created_at)
this.media_attachments =

View File

@ -203,6 +203,20 @@
android:text="@string/favourite"
android:textAllCaps="false" />
<Button
android:id="@+id/btnBookmarkAnotherAccount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/btn_bg_transparent"
android:gravity="start|center_vertical"
android:minHeight="32dp"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:text="@string/bookmark"
android:textAllCaps="false" />
<Button
android:id="@+id/btnReactionAnotherAccount"
android:layout_width="match_parent"

View File

@ -14,6 +14,7 @@
<string name="account_picker_boost">どのアカウントでブーストしますか?</string>
<string name="account_picker_reaction">どのアカウントでリアクションしますか?</string>
<string name="account_picker_favourite">どのアカウントでお気に入りしますか?</string>
<string name="account_picker_bookmark">どのアカウントでブックマークしますか?</string>
<string name="account_picker_follow">どのアカウントでフォローしますか?</string>
<string name="account_picker_open_setting">どのアカウントの設定を開きますか?</string>
<string name="account_picker_open_user_who">どのアカウントでユーザ %1$s のプロフィールを確認しますか?</string>
@ -39,6 +40,7 @@
<string name="already_boosted">既にブースト済みです</string>
<string name="already_exist">既に存在します</string>
<string name="already_favourited">既にお気に入り済みです</string>
<string name="already_bookmarked">既にブックマーク済みです</string>
<string name="already_followed">既にフォローしてます</string>
<string name="already_reactioned">リアクション済みです</string>
<string name="not_reactioned">まだリアクションしてません</string>
@ -278,6 +280,13 @@
<string name="favourite_succeeded">お気に入りにしました</string>
<string name="favourited_by">トゥートをお気に入りした人</string>
<string name="favourites">お気に入り</string>
<string name="bookmark">ブックマーク</string>
<string name="bookmark_from_another_account">別アカウントでブックマーク</string>
<string name="bookmark_succeeded">ブックマークしました</string>
<string name="bookmarks">ブックマーク</string>
<string name="unbookmark_succeeded">ブックマークを解除しました</string>
<string name="federate_timeline">連合タイムライン</string>
<string name="field_name1">ラベル1</string>
<string name="field_name2">ラベル2</string>

View File

@ -81,6 +81,7 @@
<string name="confirm_unboost_from">Unboost this status from %1$s ?</string>
<string name="confirm_favourite_from">Favourite this status from %1$s ? Notification will be sent to its author.</string>
<string name="confirm_unfavourite_from">Unfavourite this status from %1$s ?</string>
<string name="confirm_unbookmark_from">Unbookmark this status from %1$s ?</string>
<string name="display_name_favourited_by">%1$s favourited</string>
<string name="display_name_boosted_by">%1$s boosted</string>
<string name="display_name_replied_by">%1$s replied</string>
@ -209,6 +210,13 @@
<string name="app_was_muted">App was muted.</string>
<string name="favourite_succeeded">Favourited</string>
<string name="unfavourite_succeeded">Unfavourited</string>
<string name="bookmark">Bookmark</string>
<string name="bookmark_from_another_account">Bookmark from another account</string>
<string name="bookmark_succeeded">Bookmarked</string>
<string name="bookmarks">Bookmarks</string>
<string name="unbookmark_succeeded">Unbookmarked</string>
<string name="boost_succeeded">Boosted</string>
<string name="unboost_succeeded">Unboosted</string>
<string name="reaction_succeeded">Reacted</string>
@ -287,6 +295,7 @@
<string name="account_picker_add_timeline_of">Account to open %1$s?</string>
<string name="account_picker_open_setting">Account to open setting?</string>
<string name="account_picker_favourite">Account to favourite?</string>
<string name="account_picker_bookmark">Account to bookmark?</string>
<string name="account_picker_boost">Account to boost?</string>
<string name="account_picker_reaction">Account to react?</string>
<string name="account_picker_follow">Account to follow?</string>
@ -331,6 +340,7 @@
<string name="dont_refresh_on_activity_resume">Disable auto-refresh when returning to the app.</string>
<string name="status_id_conversion_failed">Could not convert status ID.</string>
<string name="already_favourited">Already favourited.</string>
<string name="already_bookmarked">Already bookmarked.</string>
<string name="already_boosted">Already boosted.</string>
<string name="dont_screen_off">Keep the screen on when using the app.</string>
<string name="hide_media_default">Hide all media</string>