(Mastodon 3.2)プロフカラムで覚え書き(Personal notes)を記録/表示できる。覚書は記録した本人だけが閲覧できる。

This commit is contained in:
tateisu 2020-07-19 21:28:12 +09:00
parent 1f64df766e
commit bab7bfdfd8
16 changed files with 183 additions and 42 deletions

View File

@ -250,7 +250,7 @@ class ActHighlightWordList : AppCompatActivity(), View.OnClickListener {
}
private fun create() {
DlgTextInput.show(this, getString(R.string.new_item), "", object : DlgTextInput.Callback {
DlgTextInput.show(this, getString(R.string.new_item), "", callback = object : DlgTextInput.Callback {
override fun onEmptyError() {
showToast(this@ActHighlightWordList, true, R.string.word_empty)
}

View File

@ -2178,7 +2178,7 @@ class ActMain : AsyncActivity()
this,
getString(R.string.access_token_or_api_token),
null,
object : DlgTextInput.Callback {
callback = object : DlgTextInput.Callback {
override fun onOK(dialog : Dialog, text : String) {
checkAccessToken(null, dialog, sa.host, text, sa)
}

View File

@ -1757,7 +1757,7 @@ class ActPost : AsyncActivity(),
this,
getString(R.string.attachment_description),
a.description,
object : DlgTextInput.Callback {
callback = object : DlgTextInput.Callback {
override fun onOK(dialog : Dialog, text : String) {
setAttachmentDescription(pa, dialog, text)
}

View File

@ -134,8 +134,9 @@ class App1 : Application() {
// 2019/12/18 44=> 45 SavedAccount テーブルに項目追加。
// 2019/12/18 44=> 46 SavedAccount テーブルに項目追加。
// 2020/6/8 46 => 54 別ブランチで色々してた。このブランチには影響ないが onDowngrade()を実装してないので上げてしまう
// 2020/7/19 54=>55 UserRelation テーブルに項目追加。
internal const val DB_VERSION = 54
internal const val DB_VERSION = 55
private val tableList = arrayOf(
LogData,

View File

@ -1706,35 +1706,31 @@ class Column(
// リロード不要なら何もしない
null
} else if(isMisskey) {
val result = client.request(
client.request(
PATH_MISSKEY_PROFILE,
access_info.putMisskeyApiToken().apply {
put("userId", profile_id)
}.toPostRequestBuilder()
)
// ユーザリレーションの取り扱いのため、別のparserを作ってはいけない
parser.misskeyDecodeProfilePin = true
try {
val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject))
if(a != null) {
)?.also{ result1 ->
// ユーザリレーションの取り扱いのため、別のparserを作ってはいけない
parser.misskeyDecodeProfilePin = true
try {
TootAccountRef.mayNull(parser, parser.account(result1.jsonObject))?.also{ a->
this.who_account = a
client.publishApiProgress("") // カラムヘッダの再表示
}
} finally {
parser.misskeyDecodeProfilePin = false
}
}
} else {
client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id))?.also{result1 ->
TootAccountRef.mayNull(parser, parser.account(result1.jsonObject))?.also{ a->
this.who_account = a
client.publishApiProgress("") // カラムヘッダの再表示
}
} finally {
parser.misskeyDecodeProfilePin = false
}
result
} else {
val result = client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id))
val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject))
if(a != null) {
this.who_account = a
client.publishApiProgress("") // カラムヘッダの再表示
}
result
}
}
@ -1781,7 +1777,8 @@ class Column(
// parser内部にアカウントIDとRelationのマップが生成されるので、それをデータベースに記録する
run {
val now = System.currentTimeMillis()
val who_list = parser.misskeyUserRelationMap.entries.toMutableList()
val who_list =
parser.misskeyUserRelationMap.entries.toMutableList()
var start = 0
val end = who_list.size
while(start < end) {

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter
import android.app.Dialog
import android.graphics.Color
import android.text.SpannableStringBuilder
import android.text.Spanned
@ -9,10 +10,11 @@ import android.widget.*
import jp.juggler.emoji.EmojiMap
import jp.juggler.subwaytooter.action.Action_Follow
import jp.juggler.subwaytooter.action.Action_User
import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap
import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAccountRef
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.dialog.DlgTextInput
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.createSpan
import jp.juggler.subwaytooter.table.AcctColor
@ -68,6 +70,9 @@ internal class ViewHolderHeaderProfile(
private val density : Float
private val btnMore : ImageButton
private val tvPersonalNotes : TextView
private val btnPersonalNotesEdit : ImageButton
init {
ivBackground = viewRoot.findViewById(R.id.ivBackground)
llProfile = viewRoot.findViewById(R.id.llProfile)
@ -95,18 +100,28 @@ internal class ViewHolderHeaderProfile(
ivMovedBy = viewRoot.findViewById(R.id.ivMovedBy)
llFields = viewRoot.findViewById(R.id.llFields)
tvPersonalNotes = viewRoot.findViewById(R.id.tvPersonalNotes)
btnPersonalNotesEdit = viewRoot.findViewById(R.id.btnPersonalNotesEdit)
density = tvDisplayName.resources.displayMetrics.density
ivBackground.setOnClickListener(this)
btnFollowing.setOnClickListener(this)
btnFollowers.setOnClickListener(this)
btnStatusCount.setOnClickListener(this)
btnMore.setOnClickListener(this)
btnFollow.setOnClickListener(this)
tvRemoteProfileWarning.setOnClickListener(this)
btnMoved.setOnClickListener(this)
llMoved.setOnClickListener(this)
for(v in arrayOf(
ivBackground,
btnFollowing,
btnFollowers,
btnStatusCount,
btnMore,
btnFollow,
tvRemoteProfileWarning,
btnPersonalNotesEdit,
btnMoved,
llMoved,
btnPersonalNotesEdit
)) {
v.setOnClickListener(this)
}
btnMoved.setOnLongClickListener(this)
btnFollow.setOnLongClickListener(this)
@ -132,6 +147,8 @@ internal class ViewHolderHeaderProfile(
private var contentColor = 0
private var relation : UserRelation? = null
override fun bindData(column : Column) {
super.bindData(column)
@ -141,6 +158,7 @@ internal class ViewHolderHeaderProfile(
if(! f.isNaN()) {
tvMovedName.textSize = f
tvMoved.textSize = f
tvPersonalNotes.textSize = f
}
f = activity.acct_font_size_sp
@ -159,6 +177,7 @@ internal class ViewHolderHeaderProfile(
val contentColor = column.getContentColor()
this.contentColor = contentColor
tvPersonalNotes.textColor = contentColor
tvMoved.textColor = contentColor
tvMovedName.textColor = contentColor
tvDisplayName.textColor = contentColor
@ -175,7 +194,15 @@ internal class ViewHolderHeaderProfile(
color = contentColor,
alphaMultiplier = Styler.boost_alpha
)
setIconDrawableId(
activity,
btnPersonalNotesEdit,
R.drawable.ic_edit,
color = contentColor,
alphaMultiplier = Styler.boost_alpha
)
val acctColor = column.getAcctColor()
tvCreated.textColor = acctColor
tvMovedAcct.textColor = acctColor
@ -200,6 +227,7 @@ internal class ViewHolderHeaderProfile(
llFields.removeAllViews()
if(who == null) {
relation = null
tvCreated.text = ""
tvLastStatusAt.vg(false)
ivBackground.setImageDrawable(null)
@ -345,6 +373,8 @@ internal class ViewHolderHeaderProfile(
}
val relation = UserRelation.load(access_info.db_id, who.id)
this.relation = relation
Styler.setFollowIcon(
activity,
btnFollow,
@ -355,6 +385,8 @@ internal class ViewHolderHeaderProfile(
alphaMultiplier = Styler.boost_alpha
)
tvPersonalNotes.text = relation.note ?: ""
showMoved(who, who.movedRef)
val fields = whoDetail?.fields ?: who.fields
@ -550,6 +582,53 @@ internal class ViewHolderHeaderProfile(
)
}
}
R.id.btnPersonalNotesEdit -> whoRef?.let{ whoRef->
val who = whoRef.get()
val relation = this.relation
val lastColumn = column
DlgTextInput.show(
activity,
AcctColor.getStringWithNickname(activity,R.string.personal_notes_of,who.acct),
relation?.note ?: "",
allowEmpty = true,
callback = object: DlgTextInput.Callback{
override fun onEmptyError() {
}
override fun onOK(dialog : Dialog, text : String) {
TootTaskRunner(activity).run(column.access_info,object:TootTask{
override fun background(client : TootApiClient) : TootApiResult? {
if(access_info.isPseudo)
return TootApiResult("Personal notes is not supported on pseudo account.")
if(access_info.isMisskey)
return TootApiResult("Personal notes is not supported on Misskey account.")
return client.request("/api/v1/accounts/${who.id}/note",
jsonObject {
put("comment",text)
}.toPostRequestBuilder()
)
}
override fun handleResult(result : TootApiResult?) {
if(result==null) return
if(result.error!=null)
showToast(activity,true,result.error)
else{
relation?.note = text
dialog.dismissSafe()
if(lastColumn==column) bindData(column)
}
}
})
}
}
)
}
}
}

View File

@ -93,7 +93,7 @@ object Action_Account {
activity,
activity.getString(R.string.access_token_or_api_token),
null,
object : DlgTextInput.Callback {
callback = object : DlgTextInput.Callback {
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun onOK(

View File

@ -140,7 +140,7 @@ object Action_List {
activity,
activity.getString(R.string.rename),
item.title,
object : DlgTextInput.Callback {
callback = object : DlgTextInput.Callback {
override fun onEmptyError() {
showToast(activity, false, R.string.list_name_empty)
}

View File

@ -39,6 +39,9 @@ class TootRelationShip(parser:TootParser,src : JsonObject) {
// misskey用
val requested_by : Boolean
// (Mastodon 3.2)
var note : String? = null
init {
@ -91,6 +94,7 @@ class TootRelationShip(parser:TootParser,src : JsonObject) {
this.muting = src.optBoolean("muting")
this.requested = src.optBoolean("requested")
this.endorsed = src.optBoolean("endorsed")
this.note = src.optString( "note")
// https://github.com/tootsuite/mastodon/commit/9745de883b198375ba23f7fde879f6d75ce2df0f
// Mastodon 2.8.0から

View File

@ -265,7 +265,7 @@ class DlgListMember(
activity,
activity.getString(R.string.list_create),
null,
object : DlgTextInput.Callback {
callback = object : DlgTextInput.Callback {
override fun onEmptyError() {
showToast(activity, false, R.string.list_name_empty)

View File

@ -24,6 +24,7 @@ object DlgTextInput {
activity : Activity,
caption : CharSequence,
initial_text : CharSequence?,
allowEmpty : Boolean = false,
callback : Callback
) {
val view = activity.layoutInflater.inflate(R.layout.dlg_text_input, null, false)
@ -51,7 +52,7 @@ object DlgTextInput {
btnOk.setOnClickListener {
val token = etInput.text.toString().trim { it <= ' ' }
if( token.isEmpty() ) {
if( token.isEmpty() && !allowEmpty ) {
callback.onEmptyError()
} else {
callback.onOK(dialog, token)

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter.table
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
@ -21,3 +22,6 @@ interface TableCompanion {
fun onDBCreate(db : SQLiteDatabase)
fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int)
}
fun ContentValues.putOrNull(key : String, value : String?) =
if(value == null) putNull(key) else put(key, value)

View File

@ -13,6 +13,7 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip
import jp.juggler.util.JsonObject
import jp.juggler.util.LogCategory
import jp.juggler.util.getInt
import jp.juggler.util.getStringOrNull
class UserRelation {
@ -26,6 +27,8 @@ class UserRelation {
var following_reblogs : Int = 0 // このユーザからのブーストをTLに表示する
var endorsed : Boolean = false // ユーザをプロフィールで紹介する
var note : String? = null
// 認証ユーザからのフォロー状態
fun getFollowing(who : TootAccount?) : Boolean {
return if(requested && ! following && who != null && ! who.locked) true else following
@ -62,6 +65,7 @@ class UserRelation {
private const val COL_ENDORSED = "endorsed"
private const val COL_BLOCKED_BY = "blocked_by"
private const val COL_REQUESTED_BY = "requested_by"
private const val COL_NOTE = "note"
private const val DB_ID_PSEUDO = - 2L
@ -83,6 +87,7 @@ class UserRelation {
,$COL_ENDORSED integer default 0
,$COL_BLOCKED_BY integer default 0
,$COL_REQUESTED_BY integer default 0
,$COL_NOTE text default null
)"""
)
db.execSQL(
@ -119,6 +124,13 @@ class UserRelation {
log.trace(ex)
}
}
if(oldVersion < 55 && newVersion >= 55) {
try {
db.execSQL("alter table $table add column $COL_NOTE text default null")
} catch(ex : Throwable) {
log.trace(ex)
}
}
}
fun deleteOld(now : Long) {
@ -155,6 +167,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
App1.database.replaceOrThrow(table, null, cv)
val key = String.format("%s:%s", db_id, whoId)
@ -184,6 +197,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
App1.database.replaceOrThrow(table, null, cv)
val key = String.format("%s:%s", db_id, id)
mMemoryCache.remove(key)
@ -216,6 +230,7 @@ class UserRelation {
cv.put(COL_REQUESTED, src.requested.b2i())
cv.put(COL_FOLLOWING_REBLOGS, src.showing_reblogs)
cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replaceOrThrow(table, null, cv)
}
@ -267,6 +282,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replaceOrThrow(table, null, cv)
}
bOK = true
@ -307,6 +323,7 @@ class UserRelation {
cv.put(COL_ENDORSED, src.endorsed.b2i())
cv.put(COL_BLOCKED_BY, src.blocked_by.b2i())
cv.put(COL_REQUESTED_BY, src.requested_by.b2i())
cv.putOrNull(COL_NOTE, src.note)
db.replace(table, null, cv)
}
bOK = true
@ -365,6 +382,7 @@ class UserRelation {
dst.endorsed = cursor.getBoolean(COL_ENDORSED)
dst.blocked_by = cursor.getBoolean(COL_BLOCKED_BY)
dst.requested_by = cursor.getBoolean(COL_REQUESTED_BY)
dst.note = cursor.getStringOrNull(COL_NOTE)
return dst
}
}

View File

@ -220,6 +220,39 @@
tools:text="xxxx-xx-xx xx:xx:xx"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="start"
android:text="@string/personal_notes"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|top"
android:orientation="horizontal"
>
<TextView
android:id="@+id/tvPersonalNotes"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="start"
android:padding="12dp"
android:text="@string/personal_notes"
/>
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/btn_bg_transparent_round6dp"
android:layout_marginStart="4dp"
android:id="@+id/btnPersonalNotesEdit"
android:src="@drawable/ic_edit"
/>
</LinearLayout>
<TextView
android:id="@+id/tvRemoteProfileWarning"
android:layout_width="match_parent"

View File

@ -1028,4 +1028,6 @@
<string name="verified_link_background_color">認証済みリンクの背景色 (アプリ再起動が必要)</string>
<string name="verified_link_foreground_color">認証済みリンクの文字色 (アプリ再起動が必要)</string>
<string name="screen_bottom_padding">画面下部の余白(単位:dp。デフォルト:0。アプリ再起動が必要)</string>
<string name="personal_notes">覚え書き</string>
<string name="personal_notes_of">%1$sに関する覚え書き</string>
</resources>

View File

@ -1035,4 +1035,6 @@
<string name="verified_link_background_color">Verified link background color (app restart required)</string>
<string name="verified_link_foreground_color">Verified link foreground color (app restart required)</string>
<string name="screen_bottom_padding">Screen bottom padding (Unit: dp. Default:0. App restart required)</string>
<string name="personal_notes">Personal notes</string>
<string name="personal_notes_of">Personal notes of %1$s</string>
</resources>