Misskeyで同一の添付データが複数あると投稿に失敗して、なぜかジョブが残り続ける問題の対策。Misskeyでも添付データの説明文を指定できるようにする。

This commit is contained in:
tateisu 2022-05-30 21:32:12 +09:00
parent 7ba02264cc
commit e182127494
15 changed files with 109 additions and 91 deletions

View File

@ -1,8 +1,8 @@
package jp.juggler.subwaytooter.api.entity
import android.content.SharedPreferences
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.util.*
class TootAttachment : TootAttachmentLike {
@ -120,12 +120,9 @@ class TootAttachment : TootAttachmentLike {
remote_url = url
text_url = url
description = arrayOf(
src.string("name"),
src.string("comment")
)
.filterNotNull()
.joinToString(" / ")
description = src.string("comment")?.notBlank()
?: src.string("name")?.notBlank()
focusX = 0f
focusY = 0f

View File

@ -909,15 +909,28 @@ class AttachmentUploader(
var resultAttachment: TootAttachment? = null
val result = try {
context.runApiTask(account) { client ->
client.request(
"/api/v1/media/$attachmentId",
jsonObject {
put("description", description)
if( account.isMisskey){
client.request(
"/api/drive/files/update",
account.putMisskeyApiToken().apply {
put("fileId", attachmentId.toString())
put("comment",description)
}.toPostRequestBuilder()
)?.also { result ->
resultAttachment =
parseItem(::TootAttachment, ServiceType.MISSKEY, result.jsonObject)
}
}else {
client.request(
"/api/v1/media/$attachmentId",
jsonObject {
put("description", description)
}
.toPutRequestBuilder()
)?.also { result ->
resultAttachment =
parseItem(::TootAttachment, ServiceType.MASTODON, result.jsonObject)
}
.toPutRequestBuilder()
)?.also { result ->
resultAttachment =
parseItem(::TootAttachment, ServiceType.MASTODON, result.jsonObject)
}
}
} catch (ex: Throwable) {

View File

@ -78,53 +78,19 @@ class PostImpl(
else -> 25 // TootPollsType.Mastodon
}
private fun preCheckPollItemOne(list: List<String>, idx: Int, item: String): Boolean {
private fun preCheckPollItemOne(list: List<String>, idx: Int, item: String) {
// 選択肢が長すぎる
val cpCount = item.codePointCount(0, item.length)
if (cpCount > choiceMaxChars) {
val over = cpCount - choiceMaxChars
activity.showToast(true, R.string.enquete_item_too_long, idx + 1, over)
return false
activity.errorString(R.string.enquete_item_too_long, idx + 1, over)
}
// 他の項目と重複している
if ((0 until idx).any { list[it] == item }) {
activity.showToast(true, R.string.enquete_item_duplicate, idx + 1)
return false
activity.errorString(R.string.enquete_item_duplicate, idx + 1)
}
return true
}
private fun preCheck(): Boolean {
if (content.isEmpty() && attachmentList == null) {
activity.showToast(true, R.string.post_error_contents_empty)
return false
}
// nullはCWチェックなしを示す // nullじゃなくてカラならエラー
if (spoilerText != null && spoilerText.isEmpty()) {
activity.showToast(true, R.string.post_error_contents_warning_empty)
return false
}
if (enqueteItems != null) {
if (enqueteItems.size < 2) {
activity.showToast(true, R.string.enquete_item_is_empty, enqueteItems.size + 1)
return false
}
enqueteItems.forEachIndexed { i, v ->
if (!preCheckPollItemOne(enqueteItems, i, v)) return false
}
}
if (scheduledAt != 0L && account.isMisskey) {
activity.showToast(true, "misskey has no scheduled status API")
return false
}
return true
}
private var resultStatus: TootStatus? = null
@ -401,7 +367,39 @@ class PostImpl(
}
suspend fun runSuspend(): PostResult {
if (!preCheck()) throw CancellationException("preCheck failed.")
if (account.isMisskey){
val duplicateCheck = buildMap {
attachmentList?.forEach {
put( it.id.toString(), (get(it.id.toString())?:0)+1)
}
}
if( duplicateCheck.values.all { it >=2 }){
activity.errorString(R.string.post_error_attachments_duplicated)
}
}
if (content.isEmpty() && attachmentList == null) {
activity.errorString(R.string.post_error_contents_empty)
}
// nullはCWチェックなしを示す // nullじゃなくてカラならエラー
if (spoilerText != null && spoilerText.isEmpty()) {
activity.errorString(R.string.post_error_contents_warning_empty)
}
if (!enqueteItems.isNullOrEmpty()) {
if (enqueteItems.size < 2) {
activity.errorString(R.string.enquete_item_is_empty, enqueteItems.size + 1)
}
enqueteItems.forEachIndexed { i, v ->
preCheckPollItemOne(enqueteItems, i, v)
}
}
if (scheduledAt != 0L && account.isMisskey) {
error("misskey has no scheduled status API")
}
if (PrefB.bpWarnHashtagAsciiAndNonAscii()) {
TootTag.findHashtags(content, account.isMisskey)
@ -449,6 +447,7 @@ class PostImpl(
// 投稿中に再度投稿ボタンが押された
if (postJob?.get()?.isActive == true) {
log.e("other postJob is active!")
activity.showToast(false, R.string.post_button_tapped_repeatly)
throw CancellationException("preCheck failed.")
}
@ -458,6 +457,7 @@ class PostImpl(
val delta = now - lastPostTapped
lastPostTapped = now
if (delta < 1000L) {
log.e("lastPostTapped within 1 sec!")
activity.showToast(false, R.string.post_button_tapped_repeatly)
throw CancellationException("post_button_tapped_repeatly")
}
@ -576,7 +576,7 @@ class PostImpl(
status != null ->
PostResult.Normal(account, status)
else -> error( result.error ?: "(result.error is null)")
else -> error(result.error ?: "(result.error is null)")
}
}
}

View File

@ -31,17 +31,6 @@ fun launchMain(block: suspend CoroutineScope.() -> Unit): Job =
}
}
fun AppCompatActivity.launchAndShowError(block: suspend CoroutineScope.() -> Unit): Job =
lifecycleScope.launch() {
try {
block()
} catch (ex: Throwable) {
showError(ex)
}
}
// Default Dispatcherで動作するコルーチンを起動して、終了を待たずにリターンする。
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
fun launchDefault(block: suspend CoroutineScope.() -> Unit): Job =
@ -71,6 +60,17 @@ fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
fun <T : Any?> asyncIO(block: suspend CoroutineScope.() -> T): Deferred<T> =
EndlessScope.async(block = block, context = Dispatchers.IO)
fun AppCompatActivity.launchAndShowError(
errorCaption: String? = null,
block: suspend CoroutineScope.() -> Unit,
): Job = lifecycleScope.launch() {
try {
block()
} catch (ex: Throwable) {
showError(ex, errorCaption)
}
}
/////////////////////////////////////////////////////////////////////////
suspend fun <T : Any?> AppCompatActivity.runWithProgress(
@ -79,7 +79,7 @@ suspend fun <T : Any?> AppCompatActivity.runWithProgress(
afterProc: suspend CoroutineScope.(result: T) -> Unit = {},
progressInitializer: suspend CoroutineScope.(ProgressDialogEx) -> Unit = {},
preProc: suspend CoroutineScope.() -> Unit = {},
postProc: suspend CoroutineScope.() -> Unit = {}
postProc: suspend CoroutineScope.() -> Unit = {},
) {
coroutineScope {
if (!isMainThread) error("runWithProgress: not main thread.")

View File

@ -4,8 +4,11 @@ import android.content.res.Resources
import android.util.Log
import androidx.annotation.StringRes
fun Throwable.withCaption(caption: String) =
"$caption :${javaClass.simpleName} $message"
fun Throwable.withCaption(caption: String?=null) =
when {
caption.isNullOrBlank() -> "${javaClass.simpleName} $message"
else -> "$caption :${javaClass.simpleName} $message"
}
fun Throwable.withCaption(resources: Resources, stringId: Int, vararg args: Any) =
"${resources.getString(stringId, *args)}: ${javaClass.simpleName} $message"
@ -58,7 +61,7 @@ class LogCategory(category: String) {
///////////////////////////////
// Throwable + string
private fun msg(priority: Int, ex: Throwable, caption: String = "exception.") =
private fun msg(priority: Int, ex: Throwable, caption: String? =null) =
msg(priority, ex.withCaption(caption))
fun e(ex: Throwable, caption: String = "exception") = msg(Log.ERROR, ex, caption)

View File

@ -2,6 +2,7 @@ package jp.juggler.util
import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import jp.juggler.subwaytooter.R
@ -49,7 +50,7 @@ internal fun showToastImpl(context: Context, bLong: Boolean, message: String): B
fun Context.showToast(bLong: Boolean, caption: String?): Boolean =
showToastImpl(this, bLong, caption ?: "(null)")
fun Context.showToast(ex: Throwable, caption: String = "error."): Boolean =
fun Context.showToast(ex: Throwable, caption: String? = null): Boolean =
showToastImpl(this, true, ex.withCaption(caption))
fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean =
@ -58,8 +59,9 @@ fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean
fun Context.showToast(ex: Throwable, stringId: Int, vararg args: Any): Boolean =
showToastImpl(this, true, ex.withCaption(resources, stringId, *args))
fun AppCompatActivity.showError(ex: Throwable, caption: String = "error.") {
log.e(ex)
fun AppCompatActivity.showError(ex: Throwable, caption: String? = null) {
log.e(ex, caption ?: "(showError)")
// キャンセル例外はUIに表示しない
if (ex is CancellationException) return
@ -70,10 +72,8 @@ fun AppCompatActivity.showError(ex: Throwable, caption: String = "error.") {
listOf(
caption,
when (ex) {
is IllegalStateException ->
null
else ->
ex.javaClass.simpleName
is IllegalStateException -> null
else -> ex.javaClass.simpleName
},
ex.message,
)
@ -81,8 +81,11 @@ fun AppCompatActivity.showError(ex: Throwable, caption: String = "error.") {
.joinToString("\n")
)
.setPositiveButton(R.string.ok, null)
} catch (ex: Throwable) {
log.e(ex)
.show()
} catch (ignored: Throwable) {
showToast(ex, caption)
}
}
fun Context.errorString(@StringRes stringId: Int, vararg args: Any?): Nothing =
error(getString(stringId, args))

View File

@ -557,7 +557,7 @@
<string name="not_blocked">No blocada.</string>
<string name="not_muted">No silenciada.</string>
<string name="media_description">(adjunció %1$d) %2$s</string>
<string name="set_description">crea una descripció (Mastodont 2.0+)</string>
<string name="set_description">crea una descripció</string>
<string name="attachment_description_cant_edit_while_uploading">La descripció de l\'adjunció no es pot editar mentre es carrega.</string>
<string name="attachment_description">descripció d\'adjunció</string>
<string name="description_empty">no hi ha descripció.</string>

View File

@ -939,7 +939,7 @@
<string name="description_empty">未指定描述.</string>
<string name="attachment_description">附件描述</string>
<string name="attachment_description_cant_edit_while_uploading">正在上传时无法编辑附件描述.</string>
<string name="set_description">设置描述(Mastodon 2.0+)</string>
<string name="set_description">设置描述</string>
<string name="media_description">(附件%1$d)%2$s</string>
<string name="not_muted">未被静音.</string>
<string name="not_blocked">未被阻止.</string>

View File

@ -332,7 +332,7 @@
<string name="emoji">Emoji</string>
<string name="not_blocked">Heb ei flocio.</string>
<string name="not_muted">Heb ei dawelu.</string>
<string name="set_description">gosod disgrifiad(Mastodon 2.0 neu hwyrach)</string>
<string name="set_description">gosod disgrifiad</string>
<string name="attachment_description_cant_edit_while_uploading">Ni ellir golygu disgrfiad yr atodiad tra\'n uwchlwytho.</string>
<string name="attachment_description">disgrifiad atodiad</string>
<string name="description_empty">disgrifiad heb ei bennu.</string>

View File

@ -845,7 +845,7 @@
<string name="lists">Listen</string>
<string name="operation_succeeded">Fertig.</string>
<string name="attachment_description_sending">sende Beschreibung des Anhangs…</string>
<string name="set_description">Beschreibung festlegen (Mastodon 2.0 und später)</string>
<string name="set_description">Beschreibung festlegen</string>
<string name="attachment_description">Beschreibung des Anhangs</string>
<string name="attachment_description_cant_edit_while_uploading">Die Beschreibung des Anhangs kann beim Hochladen nicht mehr geändert werden.</string>
<string name="media_description">(Anhang %1$d) %2$s</string>

View File

@ -524,7 +524,7 @@
<string name="not_blocked">Non bloqué.</string>
<string name="not_muted">Pas mis en sourdine.</string>
<string name="media_description">(pièce jointe %1$d) %2$s</string>
<string name="set_description">ajouter une description (Mastodon 2.0+)</string>
<string name="set_description">ajouter une description</string>
<string name="attachment_description_cant_edit_while_uploading">La description de la pièce jointe ne peut être modifiée pendant le téléversement.</string>
<string name="attachment_description">description de la pièce jointe</string>
<string name="description_empty">aucune description.</string>

View File

@ -664,7 +664,7 @@
<string name="sensitive_content_default_open">NSFWな添付データを隠さない</string>
<string name="cw_default_open">CWされたコンテンツを隠さない</string>
<string name="server_confirmed">サーバを確認しました</string>
<string name="set_description">説明文を設定(マストドン2.0以降)</string>
<string name="set_description">説明文を設定</string>
<string name="set_focus_point">焦点を設定 (マストドン2.3以降)</string>
<string name="setting">設定</string>
<string name="share_url">URLを共有</string>
@ -1146,4 +1146,5 @@
<string name="error">エラー</string>
<string name="emoji_picker_custom_of">カスタム: %1$s</string>
<string name="others" >その他</string>
<string name="post_error_attachments_duplicated">Misskeyは添付データの重複を許可していません。</string>
</resources>

View File

@ -535,7 +535,7 @@
<string name="not_blocked">차단 안 됨.</string>
<string name="not_muted">음소거 안 됨.</string>
<string name="media_description">(첨부 %1$d) %2$s</string>
<string name="set_description">설명을 설정 (마스토돈 2.0 이상)</string>
<string name="set_description">설명을 설정</string>
<string name="attachment_description_cant_edit_while_uploading">첨부파일 설명은 올리는 중에는 편집할 수 없습니다.</string>
<string name="attachment_description">첨부파일 설명</string>
<string name="description_empty">설명이 지정되지 않았습니다.</string>

View File

@ -418,7 +418,7 @@
<string name="not_blocked">Ikke blokkert.</string>
<string name="not_muted">Ikke forstummet.</string>
<string name="media_description">(vedlegg %1$d) %2$s</string>
<string name="set_description">sett beskrivelse (Mastodon 2.0 eller senere)</string>
<string name="set_description">sett beskrivelse</string>
<string name="attachment_description">vedleggsbeskrivelse</string>
<string name="description_empty">beskrivelsen er ikke angitt.</string>
<string name="attachment_description_sending">sender vedleggsbeskrivelse…</string>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Subway Tooter</string>
<string name="action_settings">Settings</string>
<string name="account_add">Add an account</string>
@ -553,7 +553,7 @@
<string name="not_blocked">Not blocked.</string>
<string name="not_muted">Not muted.</string>
<string name="media_description">(attachment %1$d) %2$s</string>
<string name="set_description">set description (Mastodon 2.0+)</string>
<string name="set_description">set description</string>
<string name="attachment_description_cant_edit_while_uploading">Attachment description can\'t be edit while uploading.</string>
<string name="attachment_description">attachment description</string>
<string name="description_empty">description is not specified.</string>
@ -669,11 +669,11 @@
<string name="unfavourite">Unfavourite</string>
<string name="unboost">Unboost</string>
<string name="dont_retrieve_preview_card">Don\'t retrieve preview card</string>
<string name="field_name1">label1</string>
<string name="field_name1" tools:ignore="Typos">label1</string>
<string name="field_name2">label2</string>
<string name="field_name3">label3</string>
<string name="field_name4">label4</string>
<string name="field_value1">value1</string>
<string name="field_value1" tools:ignore="Typos">value1</string>
<string name="field_value2">value2</string>
<string name="field_value3">value3</string>
<string name="field_value4">value4</string>
@ -1154,5 +1154,6 @@
<string name="categories">Categories</string>
<string name="error">Error</string>
<string name="emoji_picker_custom_of">Custom: %1$s</string>
<string name="others" >Others</string>
<string name="others">Others</string>
<string name="post_error_attachments_duplicated">Misskey does not allow duplicate in attachments.</string>
</resources>