(Misskey)show and vote enquete
This commit is contained in:
parent
e1d35f6ec7
commit
8a32a54041
|
@ -806,19 +806,22 @@ internal class ItemViewHolder(
|
|||
|
||||
// ニコフレのアンケートの表示
|
||||
val enquete = status.enquete
|
||||
if(enquete != null && NicoEnquete.TYPE_ENQUETE == enquete.type) {
|
||||
val question = enquete.decoded_question
|
||||
val items = enquete.items
|
||||
|
||||
if(question.isNotBlank()) content = question
|
||||
if(items != null) {
|
||||
val now = System.currentTimeMillis()
|
||||
var n = 0
|
||||
for(item in items) {
|
||||
makeEnqueteChoiceView(enquete, now, n ++, item.decoded_text)
|
||||
if(enquete != null ){
|
||||
if( access_info.isMisskey || NicoEnquete.TYPE_ENQUETE == enquete.type) {
|
||||
val question = enquete.decoded_question
|
||||
val items = enquete.items
|
||||
|
||||
if(question.isNotBlank()) content = question
|
||||
if(items != null) {
|
||||
val now = System.currentTimeMillis()
|
||||
var n = 0
|
||||
for(item in items) {
|
||||
makeEnqueteChoiceView(enquete, now, n ++, item)
|
||||
}
|
||||
}
|
||||
|
||||
if(!access_info.isMisskey) makeEnqueteTimerView(enquete)
|
||||
}
|
||||
makeEnqueteTimerView(enquete)
|
||||
}
|
||||
|
||||
// カードの表示(会話ビューのみ)
|
||||
|
@ -1760,10 +1763,14 @@ internal class ItemViewHolder(
|
|||
enquete : NicoEnquete,
|
||||
now : Long,
|
||||
i : Int,
|
||||
item : Spannable
|
||||
item : NicoEnquete.Choice
|
||||
) {
|
||||
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
val canVote = if( access_info.isMisskey ){
|
||||
enquete.myVoted == null
|
||||
}else{
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
enquete.myVoted == null && remain > 0L
|
||||
}
|
||||
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
|
@ -1774,11 +1781,25 @@ internal class ItemViewHolder(
|
|||
val b = Button(activity)
|
||||
b.layoutParams = lp
|
||||
b.setAllCaps(false)
|
||||
b.text = item
|
||||
|
||||
val text = if( access_info.isMisskey ){
|
||||
val sb = SpannableStringBuilder()
|
||||
.append(item.decoded_text)
|
||||
|
||||
if(enquete.myVoted != null ) {
|
||||
sb.append(" / ")
|
||||
sb.append(activity.getString(R.string.vote_count_text, item.votes))
|
||||
if(i == enquete.myVoted) sb.append(' ').append(0x2713.toChar())
|
||||
}
|
||||
sb
|
||||
}else{
|
||||
item.decoded_text
|
||||
}
|
||||
b.text = text
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extra_invalidator_list.add(invalidator)
|
||||
invalidator.register(item)
|
||||
if(remain <= 0) {
|
||||
invalidator.register(text)
|
||||
if(!canVote) {
|
||||
b.isEnabled = false
|
||||
} else {
|
||||
val accessInfo = this@ItemViewHolder.access_info
|
||||
|
@ -1807,26 +1828,40 @@ internal class ItemViewHolder(
|
|||
idx : Int
|
||||
) {
|
||||
val now = System.currentTimeMillis()
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
if(remain <= 0) {
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
if( enquete.myVoted != null ) {
|
||||
showToast(context, false, R.string.already_voted)
|
||||
return
|
||||
}
|
||||
if(!accessInfo.isMisskey){
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
if( remain <=0L) {
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
TootTaskRunner(context).run(accessInfo, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val form = JSONObject()
|
||||
try {
|
||||
form.put("item_index", Integer.toString(idx))
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "json encode failed.")
|
||||
ex.printStackTrace()
|
||||
if( accessInfo.isMisskey ){
|
||||
val params = accessInfo.putMisskeyApiToken(JSONObject())
|
||||
.put("noteId",enquete.status_id.toString())
|
||||
.put("choice",idx)
|
||||
return client.request("/api/notes/polls/vote",params.toPostRequestBuilder())
|
||||
}else{
|
||||
val form = JSONObject()
|
||||
try {
|
||||
form.put("item_index", Integer.toString(idx))
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "json encode failed.")
|
||||
ex.printStackTrace()
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder()
|
||||
.post(RequestBody.create(TootApiClient.MEDIA_TYPE_JSON, form.toString()))
|
||||
|
||||
return client.request("/api/v1/votes/" + enquete.status_id, request_builder)
|
||||
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder()
|
||||
.post(RequestBody.create(TootApiClient.MEDIA_TYPE_JSON, form.toString()))
|
||||
|
||||
return client.request("/api/v1/votes/" + enquete.status_id, request_builder)
|
||||
}
|
||||
|
||||
override fun handleResult(result : TootApiResult?) {
|
||||
|
@ -1834,12 +1869,25 @@ internal class ItemViewHolder(
|
|||
|
||||
val data = result.jsonObject
|
||||
if(data != null) {
|
||||
val message = data.parseString("message") ?: "?"
|
||||
val valid = data.optBoolean("valid")
|
||||
if(valid) {
|
||||
if(accessInfo.isMisskey){
|
||||
enquete.myVoted = idx
|
||||
val choice = enquete.items?.get(idx)
|
||||
if( choice != null ) choice.votes ++
|
||||
|
||||
// 204 no content
|
||||
showToast(context, false, R.string.enquete_voted)
|
||||
} else {
|
||||
showToast(context, true, R.string.enquete_vote_failed, message)
|
||||
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
list_adapter.notifyChange(reason = "onClickEnqueteChoice", reset = true)
|
||||
}else{
|
||||
val message = data.parseString("message") ?: "?"
|
||||
val valid = data.optBoolean("valid")
|
||||
if(valid) {
|
||||
showToast(context, false, R.string.enquete_voted)
|
||||
} else {
|
||||
showToast(context, true, R.string.enquete_vote_failed, message)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
showToast(context, true, result.error)
|
||||
|
|
|
@ -200,20 +200,37 @@ class TootApiClient(
|
|||
|
||||
fun getScopeArrayMisskey(@Suppress("UNUSED_PARAMETER") ti : TootInstance) =
|
||||
JSONArray().apply {
|
||||
|
||||
put("account-read")
|
||||
put("account-write")
|
||||
|
||||
put("note-read")
|
||||
put("note-write")
|
||||
|
||||
put("reaction-read")
|
||||
put("reaction-write")
|
||||
put("following-write")
|
||||
|
||||
put("following-read") // フォロリク申請一覧で使われていた
|
||||
put("following-write")
|
||||
|
||||
put("drive-read")
|
||||
put("drive-write")
|
||||
|
||||
put("notification-read")
|
||||
put("notification-write")
|
||||
|
||||
put("favorite-read")
|
||||
put("favorite-write")
|
||||
|
||||
put("account/read")
|
||||
put("account/write")
|
||||
|
||||
put("messaging-read")
|
||||
put("messaging-write")
|
||||
|
||||
put("vote-read")
|
||||
put("vote-write")
|
||||
|
||||
// https://github.com/syuilo/misskey/issues/2341
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import android.content.Context
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
@ -33,6 +34,8 @@ class NicoEnquete(
|
|||
// 結果の数値のテキスト // null or array of string
|
||||
private val ratios_text : MutableList<String>?
|
||||
|
||||
var myVoted : Int? = null
|
||||
|
||||
// 以下はJSONには存在しないが内部で使う
|
||||
val time_start : Long
|
||||
val status_id : EntityId
|
||||
|
@ -45,40 +48,30 @@ class NicoEnquete(
|
|||
if(parser.serviceType == ServiceType.MISSKEY) {
|
||||
|
||||
this.items = parseChoiceListMisskey(
|
||||
parser.context,
|
||||
status,
|
||||
src.optJSONArray("items")
|
||||
|
||||
src.optJSONArray("choices")
|
||||
)
|
||||
var hasVoteResult = false
|
||||
|
||||
|
||||
val votesList = ArrayList<Int>()
|
||||
var votesMax = 1
|
||||
|
||||
if( items != null){
|
||||
for( choice in items){
|
||||
val votes = choice.votes
|
||||
if( votes != null ){
|
||||
hasVoteResult = true
|
||||
votesList.add(votes)
|
||||
if( votes > votesMax) votesMax = votes
|
||||
}else{
|
||||
votesList.add(0)
|
||||
}
|
||||
}
|
||||
items?.forEachIndexed { index, choice ->
|
||||
if(choice.isVoted) this.myVoted = index
|
||||
val votes = choice.votes
|
||||
votesList.add(votes)
|
||||
if(votes > votesMax) votesMax = votes
|
||||
}
|
||||
|
||||
if( hasVoteResult ){
|
||||
this.ratios = votesList.map { (it.toFloat()/votesMax.toFloat()) }.toMutableList()
|
||||
this.ratios_text = votesList.map{ parser.context.getString(R.string.vote_count_text,it)}.toMutableList()
|
||||
}else{
|
||||
|
||||
if(votesList.isNotEmpty()) {
|
||||
this.ratios = votesList.map { (it.toFloat() / votesMax.toFloat()) }.toMutableList()
|
||||
this.ratios_text =
|
||||
votesList.map { parser.context.getString(R.string.vote_count_text, it) }
|
||||
.toMutableList()
|
||||
} else {
|
||||
this.ratios = null
|
||||
this.ratios_text = null
|
||||
}
|
||||
|
||||
this.type = when(hasVoteResult){
|
||||
true -> "enquete_result"
|
||||
else-> "enquete"
|
||||
}
|
||||
this.type = NicoEnquete.TYPE_ENQUETE
|
||||
|
||||
this.question = status.content
|
||||
this.decoded_question = DecodeOptions(
|
||||
|
@ -122,9 +115,8 @@ class NicoEnquete(
|
|||
class Choice(
|
||||
val text : String,
|
||||
val decoded_text : Spannable,
|
||||
val id : EntityId? = null, // misskey
|
||||
var isVoted : Boolean = false, // misskey
|
||||
var votes : Int? = null // misskey
|
||||
var votes : Int = 0 // misskey
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
@ -159,11 +151,12 @@ class NicoEnquete(
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(
|
||||
parser : TootParser,
|
||||
status : TootStatus,
|
||||
list_attachment : ArrayList<TootAttachmentLike>?,
|
||||
src:JSONObject?
|
||||
src : JSONObject?
|
||||
) : NicoEnquete? {
|
||||
src ?: return null
|
||||
return try {
|
||||
|
@ -178,6 +171,7 @@ class NicoEnquete(
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseStringArray(src : JSONObject, name : String) : ArrayList<String>? {
|
||||
val array = src.optJSONArray(name)
|
||||
if(array != null) {
|
||||
|
@ -228,17 +222,9 @@ class NicoEnquete(
|
|||
}
|
||||
|
||||
private fun parseChoiceListMisskey(
|
||||
context : Context,
|
||||
status : TootStatus,
|
||||
choices : JSONArray?
|
||||
) : ArrayList<Choice>? {
|
||||
if(choices != null) {
|
||||
val options = DecodeOptions(
|
||||
context,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis
|
||||
)
|
||||
|
||||
val items = ArrayList<Choice>()
|
||||
for(i in 0 until choices.length()) {
|
||||
val src = choices.optJSONObject(i)
|
||||
|
@ -246,13 +232,13 @@ class NicoEnquete(
|
|||
val text = reWhitespace
|
||||
.matcher(src.parseString("text")?.sanitizeBDI() ?: "")
|
||||
.replaceAll(" ")
|
||||
val decoded_text = options.decodeEmoji(text)
|
||||
val decoded_text = SpannableString(text) // misskey ではマークダウン不可で絵文字もない
|
||||
|
||||
val dst = Choice(
|
||||
text = text,
|
||||
decoded_text = decoded_text,
|
||||
id = EntityId.mayNull(src.parseString("id")),
|
||||
votes = src.parseInt("votes"),
|
||||
// 配列インデクスと同じだった id = EntityId.mayNull(src.parseLong("id")),
|
||||
votes = src.parseInt("votes")?:0,
|
||||
isVoted = src.optBoolean("isVoted")
|
||||
)
|
||||
items.add(dst)
|
||||
|
|
|
@ -802,7 +802,7 @@ object MisskeyMarkdownDecoder {
|
|||
else -> Node(
|
||||
pos
|
||||
, matcher.end()
|
||||
, arrayOf(matcher.group(1), matcher.group(2)) // username, host
|
||||
, arrayOf(matcher.group(1), matcher.group(2)?:"") // username, host
|
||||
) {
|
||||
val username = data[0]
|
||||
val host = data[1]
|
||||
|
|
|
@ -760,7 +760,7 @@
|
|||
<string name="reaction_add">Add reaction</string>
|
||||
<string name="unknown_notification_from">unknown notification from %1$s</string>
|
||||
<string name="dont_show_reaction">Don\'t show reaction</string>
|
||||
<string name="dont_show_vote">Don\'t show vote</string>
|
||||
<string name="dont_show_vote">Don\'t show enquête</string>
|
||||
<string name="boost_button_size">Boost button size( unit:dp, default=40, app restart required)</string>
|
||||
<string name="boost_button_alignment">Boost button alignment (app restart required)</string>
|
||||
<string name="start">Start</string>
|
||||
|
@ -776,6 +776,7 @@
|
|||
<string name="background_color_follower">Toot background color of \'follower\' visibility</string>
|
||||
<string name="background_color_direct_with_user">Toot background color of \'Direct to users\' visibility</string>
|
||||
<string name="background_color_direct_no_user">Toot background color of \'Direct only me\' visibility</string>
|
||||
<string name="already_voted">Already voted.</string>
|
||||
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||
|
|
|
@ -1037,7 +1037,7 @@
|
|||
<string name="reaction_add">リアクションの追加</string>
|
||||
<string name="unknown_notification_from">%1$s からの謎の通知</string>
|
||||
<string name="dont_show_reaction">リアクションを表示しない</string>
|
||||
<string name="dont_show_vote">投票を表示しない</string>
|
||||
<string name="dont_show_vote">アンケートを表示しない</string>
|
||||
<string name="boost_button_size">ブーストボタンのサイズ(単位:dp。デフォルト:40。アプリ再起動が必要)</string>
|
||||
<string name="boost_button_alignment">ブーストボタン列の左右の配置 (アプリ再起動が必要)</string>
|
||||
<string name="start">始端</string>
|
||||
|
@ -1053,5 +1053,6 @@
|
|||
<string name="background_color_follower">トゥート背景色 \'フォロワーのみ\'</string>
|
||||
<string name="background_color_direct_with_user">トゥート背景色 \'ダイレクト(対象ユーザあり)\'</string>
|
||||
<string name="background_color_direct_no_user">トゥート背景色 \'ダイレクト(自分のみ)\'</string>
|
||||
<string name="already_voted">投票済です</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -745,7 +745,7 @@
|
|||
<string name="reaction_add">Add reaction</string>
|
||||
<string name="unknown_notification_from">unknown notification from %1$s</string>
|
||||
<string name="dont_show_reaction">Don\'t show reaction</string>
|
||||
<string name="dont_show_vote">Don\'t show vote</string>
|
||||
<string name="dont_show_vote">Don\'t show enquete</string>
|
||||
<string name="boost_button_size">Boost button size( unit:dp, default=40, app restart required)</string>
|
||||
<string name="boost_button_alignment">Boost button alignment (app restart required)</string>
|
||||
<string name="start">Start</string>
|
||||
|
@ -761,5 +761,6 @@
|
|||
<string name="background_color_follower">Toot background color of \'follower\' visibility</string>
|
||||
<string name="background_color_direct_with_user">Toot background color of \'Direct to users\' visibility</string>
|
||||
<string name="background_color_direct_no_user">Toot background color of \'Direct only me\' visibility</string>
|
||||
<string name="already_voted">Already voted.</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue