mirror of
https://github.com/tateisu/SubwayTooter
synced 2025-01-27 09:11:23 +01:00
show/vote to polls
This commit is contained in:
parent
e1be78dab5
commit
0eb8294e03
@ -165,6 +165,11 @@ class ActPost : AppCompatActivity(),
|
||||
internal const val DRAFT_REPLY_URL = "reply_url"
|
||||
internal const val DRAFT_IS_ENQUETE = "is_enquete"
|
||||
internal const val DRAFT_POLL_TYPE = "poll_type"
|
||||
internal const val DRAFT_POLL_MULTIPLE = "poll_multiple"
|
||||
internal const val DRAFT_POLL_HIDE_TOTALS = "poll_hide_totals"
|
||||
internal const val DRAFT_POLL_EXPIRE_DAY = "poll_expire_day"
|
||||
internal const val DRAFT_POLL_EXPIRE_HOUR = "poll_expire_hour"
|
||||
internal const val DRAFT_POLL_EXPIRE_MINUTE = "poll_expire_minute"
|
||||
internal const val DRAFT_ENQUETE_ITEMS = "enquete_items"
|
||||
internal const val DRAFT_QUOTED_RENOTE = "quotedRenote"
|
||||
|
||||
@ -718,11 +723,11 @@ class ActPost : AppCompatActivity(),
|
||||
val src_enquete = base_status.enquete
|
||||
val src_items = src_enquete?.items
|
||||
if(src_items != null) {
|
||||
if(src_enquete.poll_type == NicoEnquete.PollType.FriendsNico && src_enquete.type != NicoEnquete.TYPE_ENQUETE) {
|
||||
if(src_enquete.pollType == PollType.FriendsNico && src_enquete.type != NicoEnquete.TYPE_ENQUETE) {
|
||||
// フレニコAPIのアンケート結果は再編集の対象外
|
||||
} else {
|
||||
spEnquete.setSelection(
|
||||
if(src_enquete.poll_type == NicoEnquete.PollType.FriendsNico) {
|
||||
if(src_enquete.pollType == PollType.FriendsNico) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
@ -1957,7 +1962,7 @@ class ActPost : AppCompatActivity(),
|
||||
opener.open().use { inData ->
|
||||
val tmp = ByteArray(4096)
|
||||
while(true) {
|
||||
val r = inData.read(tmp, 0, tmp.size)
|
||||
val r = inData.read(tmp, 0, tmp.size)
|
||||
if(r <= 0) break
|
||||
sink.write(tmp, 0, r)
|
||||
}
|
||||
@ -2236,7 +2241,7 @@ class ActPost : AppCompatActivity(),
|
||||
when(spEnquete.selectedItemPosition) {
|
||||
1 -> {
|
||||
copyEnqueteText()
|
||||
post_helper.poll_type = NicoEnquete.PollType.Mastodon
|
||||
post_helper.poll_type = PollType.Mastodon
|
||||
post_helper.poll_expire_seconds = getExpireSeconds()
|
||||
post_helper.poll_hide_totals = cbHideTotals.isChecked
|
||||
post_helper.poll_multiple_choice = cbMultipleChoice.isChecked
|
||||
@ -2244,7 +2249,7 @@ class ActPost : AppCompatActivity(),
|
||||
|
||||
2 -> {
|
||||
copyEnqueteText()
|
||||
post_helper.poll_type = NicoEnquete.PollType.FriendsNico
|
||||
post_helper.poll_type = PollType.FriendsNico
|
||||
|
||||
}
|
||||
|
||||
@ -2388,11 +2393,17 @@ class ActPost : AppCompatActivity(),
|
||||
|
||||
json.put(DRAFT_POLL_TYPE, spEnquete.selectedItemPosition.toPollTypeString())
|
||||
|
||||
val array = JSONArray()
|
||||
for(s in str_choice) {
|
||||
array.put(s)
|
||||
}
|
||||
json.put(DRAFT_ENQUETE_ITEMS, array)
|
||||
json.put(DRAFT_POLL_MULTIPLE, cbMultipleChoice.isChecked)
|
||||
json.put(DRAFT_POLL_HIDE_TOTALS, cbHideTotals.isChecked )
|
||||
json.put(DRAFT_POLL_EXPIRE_DAY,etExpireDays.text.toString())
|
||||
json.put(DRAFT_POLL_EXPIRE_HOUR,etExpireHours.text.toString())
|
||||
json.put(DRAFT_POLL_EXPIRE_MINUTE,etExpireMinutes.text.toString())
|
||||
|
||||
json.put(DRAFT_ENQUETE_ITEMS, JSONArray().apply{
|
||||
for(s in str_choice) {
|
||||
put(s)
|
||||
}
|
||||
})
|
||||
|
||||
PostDraft.save(System.currentTimeMillis(), json)
|
||||
|
||||
@ -2563,6 +2574,13 @@ class ActPost : AppCompatActivity(),
|
||||
spEnquete.setSelection( if(bv) 2 else 0)
|
||||
}
|
||||
|
||||
cbMultipleChoice.isChecked = draft.optBoolean(DRAFT_POLL_MULTIPLE)
|
||||
cbHideTotals.isChecked = draft.optBoolean(DRAFT_POLL_HIDE_TOTALS)
|
||||
etExpireDays.setText( draft.optString(DRAFT_POLL_EXPIRE_DAY,"1"))
|
||||
etExpireHours.setText( draft.optString(DRAFT_POLL_EXPIRE_HOUR,""))
|
||||
etExpireMinutes.setText( draft.optString(DRAFT_POLL_EXPIRE_MINUTE,""))
|
||||
|
||||
|
||||
val array = draft.optJSONArray(DRAFT_ENQUETE_ITEMS)
|
||||
if(array != null) {
|
||||
var src_index = 0
|
||||
|
@ -6,16 +6,17 @@ import android.graphics.Typeface
|
||||
import android.os.SystemClock
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.util.LayoutDirection
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.google.android.flexbox.FlexWrap
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
@ -25,6 +26,7 @@ import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.drawable.PollPlotDrawable
|
||||
import jp.juggler.subwaytooter.drawable.PreviewCardBorder
|
||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||
import jp.juggler.subwaytooter.table.*
|
||||
@ -32,6 +34,7 @@ import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.view.*
|
||||
import jp.juggler.util.*
|
||||
import org.jetbrains.anko.*
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import kotlin.math.max
|
||||
|
||||
@ -707,13 +710,13 @@ internal class ItemViewHolder(
|
||||
val in_reply_to_id = item.in_reply_to_id
|
||||
val in_reply_to_account_id = item.in_reply_to_account_id
|
||||
when {
|
||||
reply != null ->{
|
||||
reply != null -> {
|
||||
showReply(
|
||||
R.drawable.ic_reply,
|
||||
R.string.reply_to,
|
||||
reply
|
||||
)
|
||||
if( colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
if(colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
|
||||
in_reply_to_id != null && in_reply_to_account_id != null -> {
|
||||
@ -722,7 +725,7 @@ internal class ItemViewHolder(
|
||||
in_reply_to_account_id,
|
||||
item
|
||||
)
|
||||
if( colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
if(colorBgArg == 0) colorBg = Pref.ipEventBgColorMention(activity.pref)
|
||||
}
|
||||
}
|
||||
showStatus(item, colorBg)
|
||||
@ -1190,20 +1193,16 @@ internal class ItemViewHolder(
|
||||
// ニコフレのアンケートの表示
|
||||
val enquete = status.enquete
|
||||
if(enquete != null) {
|
||||
if(access_info.isMisskey || NicoEnquete.TYPE_ENQUETE == enquete.type) {
|
||||
if(enquete.pollType == PollType.FriendsNico && enquete.type != NicoEnquete.TYPE_ENQUETE) {
|
||||
// フレニコの投票の結果表示は普通にテキストを表示するだけでよい
|
||||
} else {
|
||||
|
||||
// アンケートの本文を上書きする
|
||||
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)
|
||||
showEnqueteItems(status, enquete)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -2455,59 +2454,176 @@ internal class ItemViewHolder(
|
||||
})
|
||||
}
|
||||
|
||||
private fun showEnqueteItems(status : TootStatus, enquete : NicoEnquete) {
|
||||
val items = enquete.items ?: return
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
val canVote = when(enquete.pollType) {
|
||||
PollType.Mastodon -> when {
|
||||
enquete.expired -> false
|
||||
now >= enquete.expired_at -> false
|
||||
enquete.myVoted != null -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
PollType.FriendsNico -> {
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
enquete.myVoted == null && remain > 0L
|
||||
}
|
||||
|
||||
PollType.Misskey -> enquete.myVoted == null
|
||||
}
|
||||
|
||||
items.forEachIndexed { index, choice ->
|
||||
makeEnqueteChoiceView(status, enquete, canVote, index, choice)
|
||||
}
|
||||
|
||||
when(enquete.pollType) {
|
||||
PollType.Mastodon -> makeEnqueteFooterMastodon(status, enquete, canVote)
|
||||
|
||||
PollType.FriendsNico -> makeEnqueteFooterFriendsNico(enquete)
|
||||
|
||||
PollType.Misskey -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeEnqueteChoiceView(
|
||||
status : TootStatus,
|
||||
enquete : NicoEnquete,
|
||||
now : Long,
|
||||
canVote : Boolean,
|
||||
i : Int,
|
||||
item : NicoEnquete.Choice
|
||||
) {
|
||||
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 text = when(enquete.pollType) {
|
||||
PollType.Misskey -> {
|
||||
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
|
||||
}
|
||||
|
||||
PollType.FriendsNico -> {
|
||||
item.decoded_text
|
||||
}
|
||||
|
||||
PollType.Mastodon -> if(canVote) {
|
||||
item.decoded_text
|
||||
} else {
|
||||
val sb = SpannableStringBuilder()
|
||||
.append(item.decoded_text)
|
||||
if(! canVote) {
|
||||
sb.append(" / ")
|
||||
sb.append(
|
||||
when(val v = item.votes) {
|
||||
null -> activity.getString(R.string.vote_count_unavailable)
|
||||
else -> activity.getString(R.string.vote_count_text, v)
|
||||
}
|
||||
)
|
||||
}
|
||||
sb
|
||||
}
|
||||
}
|
||||
|
||||
// 投票ボタンの表示
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
if(i == 0)
|
||||
lp.topMargin = (0.5f + activity.density * 3f).toInt()
|
||||
val b = Button(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
).apply {
|
||||
if(i == 0) topMargin = (0.5f + activity.density * 3f).toInt()
|
||||
}
|
||||
|
||||
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(text)
|
||||
if(! canVote) {
|
||||
b.isEnabled = false
|
||||
} else {
|
||||
val accessInfo = this@ItemViewHolder.access_info
|
||||
b.setOnClickListener { view ->
|
||||
val context = view.context ?: return@setOnClickListener
|
||||
onClickEnqueteChoice(enquete, context, accessInfo, i)
|
||||
|
||||
val b = TextView(activity)
|
||||
b.layoutParams = lp
|
||||
|
||||
b.text = text
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extra_invalidator_list.add(invalidator)
|
||||
invalidator.register(text)
|
||||
|
||||
b.padding = (activity.density * 3f + 0.5f).toInt()
|
||||
|
||||
val ratio = when(enquete.pollType){
|
||||
PollType.Mastodon ->{
|
||||
val votesCount = enquete.votes_count ?:0
|
||||
val max = enquete.maxVotesCount ?:0
|
||||
if( max > 0 && votesCount > 0 ){
|
||||
(item.votes?:0).toFloat() / votesCount.toFloat()
|
||||
}else{
|
||||
null
|
||||
}
|
||||
}
|
||||
else->{
|
||||
val ratios = enquete.ratios
|
||||
if( ratios !=null && i <= ratios.size ){
|
||||
ratios[i]
|
||||
}else{
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ratio != null){
|
||||
b.backgroundDrawable = PollPlotDrawable(
|
||||
color = (content_color and 0xFFFFFF) or 0x20000000,
|
||||
ratio = ratio,
|
||||
isRtl = b.layoutDirection == View.LAYOUT_DIRECTION_RTL,
|
||||
startWidth = (activity.density * 2f + 0.5f).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
llExtra.addView(b)
|
||||
|
||||
} else if(enquete.multiple) {
|
||||
// 複数選択なのでチェックボックス
|
||||
val b = CheckBox(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
b.text = text
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extra_invalidator_list.add(invalidator)
|
||||
invalidator.register(text)
|
||||
if(! canVote) {
|
||||
b.isEnabled = false
|
||||
} else {
|
||||
b.isChecked = item.checked
|
||||
b.setOnCheckedChangeListener { _, checked ->
|
||||
item.checked = checked
|
||||
}
|
||||
}
|
||||
llExtra.addView(b)
|
||||
|
||||
} else {
|
||||
val b = Button(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
b.text = text
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extra_invalidator_list.add(invalidator)
|
||||
invalidator.register(text)
|
||||
if(! canVote) {
|
||||
b.isEnabled = false
|
||||
} else {
|
||||
val accessInfo = this@ItemViewHolder.access_info
|
||||
b.setOnClickListener { view ->
|
||||
val context = view.context ?: return@setOnClickListener
|
||||
onClickEnqueteChoice(status, enquete, context, accessInfo, i)
|
||||
}
|
||||
}
|
||||
llExtra.addView(b)
|
||||
}
|
||||
llExtra.addView(b)
|
||||
}
|
||||
|
||||
private fun makeEnqueteTimerView(enquete : NicoEnquete) {
|
||||
private fun makeEnqueteFooterFriendsNico(enquete : NicoEnquete) {
|
||||
val density = activity.density
|
||||
val height = (0.5f + 6 * density).toInt()
|
||||
val view = EnqueteTimerView(activity)
|
||||
@ -2517,43 +2633,133 @@ internal class ItemViewHolder(
|
||||
llExtra.addView(view)
|
||||
}
|
||||
|
||||
private fun makeEnqueteFooterMastodon(
|
||||
status : TootStatus,
|
||||
enquete : NicoEnquete,
|
||||
canVote : Boolean
|
||||
) {
|
||||
|
||||
val density = activity.density
|
||||
|
||||
if(canVote && enquete.multiple) {
|
||||
// 複数選択の投票ボタン
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
topMargin = (0.5f + density * 3f).toInt()
|
||||
}
|
||||
|
||||
val b = Button(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
b.text = activity.getString(R.string.vote_button)
|
||||
val accessInfo = this@ItemViewHolder.access_info
|
||||
b.setOnClickListener { view ->
|
||||
val context = view.context ?: return@setOnClickListener
|
||||
sendMultiple(status, enquete, context, accessInfo)
|
||||
}
|
||||
llExtra.addView(b)
|
||||
}
|
||||
|
||||
val tv = TextView(activity)
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
lp.topMargin = (0.5f + 3 * density).toInt()
|
||||
tv.layoutParams = lp
|
||||
|
||||
val sb = StringBuilder()
|
||||
|
||||
val votes_count = enquete.votes_count ?: 0
|
||||
when {
|
||||
votes_count == 1 -> sb.append(activity.getString(R.string.vote_1))
|
||||
votes_count > 1 -> sb.append(activity.getString(R.string.vote_2, votes_count))
|
||||
}
|
||||
|
||||
when(val t = enquete.expired_at) {
|
||||
|
||||
Long.MAX_VALUE -> {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if(sb.isNotEmpty()) sb.append(" ")
|
||||
sb.append(
|
||||
activity.getString(
|
||||
R.string.vote_expire_at,
|
||||
TootStatus.formatTime(activity, t, false)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tv.text = sb.toString()
|
||||
|
||||
llExtra.addView(tv)
|
||||
}
|
||||
|
||||
private fun onClickEnqueteChoice(
|
||||
status : TootStatus,
|
||||
enquete : NicoEnquete,
|
||||
context : Context,
|
||||
accessInfo : SavedAccount,
|
||||
idx : Int
|
||||
) {
|
||||
val now = System.currentTimeMillis()
|
||||
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
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
when(enquete.pollType) {
|
||||
PollType.Misskey -> {
|
||||
// Misskeyのアンケートには期限がない?
|
||||
}
|
||||
|
||||
PollType.FriendsNico -> {
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
if(remain <= 0L) {
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
PollType.Mastodon -> {
|
||||
if(enquete.expired || now >= enquete.expired_at) {
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TootTaskRunner(context).run(accessInfo, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
return if(accessInfo.isMisskey) {
|
||||
client.request(
|
||||
"/api/notes/polls/vote",
|
||||
accessInfo.putMisskeyApiToken(JSONObject())
|
||||
.put("noteId", enquete.status_id.toString())
|
||||
.put("choice", idx)
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
} else {
|
||||
client.request(
|
||||
"/api/v1/votes/${enquete.status_id}",
|
||||
JSONObject()
|
||||
.put("item_index", idx.toString())
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
}
|
||||
override fun background(client : TootApiClient) = when(enquete.pollType) {
|
||||
PollType.Misskey -> client.request(
|
||||
"/api/notes/polls/vote",
|
||||
accessInfo.putMisskeyApiToken(JSONObject())
|
||||
.put("noteId", enquete.status_id.toString())
|
||||
.put("choice", idx)
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
PollType.Mastodon -> client.request(
|
||||
"/api/v1/polls/${enquete.pollId}/votes",
|
||||
JSONObject()
|
||||
.put(
|
||||
"choices",
|
||||
JSONArray().apply {
|
||||
put(idx)
|
||||
}
|
||||
)
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
PollType.FriendsNico -> client.request(
|
||||
"/api/v1/votes/${enquete.status_id}",
|
||||
JSONObject()
|
||||
.put("item_index", idx.toString())
|
||||
.toPostRequestBuilder()
|
||||
)
|
||||
}
|
||||
|
||||
override fun handleResult(result : TootApiResult?) {
|
||||
@ -2561,23 +2767,43 @@ internal class ItemViewHolder(
|
||||
|
||||
val data = result.jsonObject
|
||||
if(data != null) {
|
||||
if(accessInfo.isMisskey) {
|
||||
if(enquete.increaseVote(activity, idx, true)) {
|
||||
when(enquete.pollType) {
|
||||
PollType.Misskey -> if(enquete.increaseVote(activity, idx, true)) {
|
||||
showToast(context, false, R.string.enquete_voted)
|
||||
|
||||
// 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)
|
||||
PollType.Mastodon -> {
|
||||
val newPoll = NicoEnquete.parse(
|
||||
TootParser(activity, accessInfo),
|
||||
status,
|
||||
status.media_attachments,
|
||||
data,
|
||||
PollType.Mastodon
|
||||
)
|
||||
if(newPoll != null) {
|
||||
status.enquete = newPoll
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
list_adapter.notifyChange(
|
||||
reason = "onClickEnqueteChoice",
|
||||
reset = true
|
||||
)
|
||||
} else if(result.error != null) {
|
||||
showToast(context, true, "response parse error")
|
||||
}
|
||||
}
|
||||
|
||||
PollType.FriendsNico -> {
|
||||
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)
|
||||
@ -2587,6 +2813,62 @@ internal class ItemViewHolder(
|
||||
})
|
||||
}
|
||||
|
||||
private fun sendMultiple(
|
||||
status : TootStatus,
|
||||
enquete : NicoEnquete,
|
||||
context : Context,
|
||||
accessInfo : SavedAccount
|
||||
) {
|
||||
val now = System.currentTimeMillis()
|
||||
if(now >= enquete.expired_at) {
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
return
|
||||
}
|
||||
|
||||
TootTaskRunner(context).run(accessInfo, object : TootTask {
|
||||
|
||||
var newPoll : NicoEnquete? = null
|
||||
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
return client.request(
|
||||
"/api/v1/polls/${enquete.pollId}/votes",
|
||||
JSONObject()
|
||||
.put("choices", JSONArray().apply {
|
||||
enquete.items?.forEachIndexed { index, choice ->
|
||||
if(choice.checked) put(index)
|
||||
}
|
||||
})
|
||||
.toPostRequestBuilder()
|
||||
)?.also { result ->
|
||||
val data = result.jsonObject
|
||||
if(data != null) {
|
||||
newPoll = NicoEnquete.parse(
|
||||
TootParser(activity, accessInfo),
|
||||
status,
|
||||
status.media_attachments,
|
||||
data,
|
||||
PollType.Mastodon
|
||||
)
|
||||
if(newPoll == null) result.setError("response parse error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleResult(result : TootApiResult?) {
|
||||
result ?: return // cancelled.
|
||||
|
||||
val newPoll = this.newPoll
|
||||
if(newPoll != null) {
|
||||
status.enquete = newPoll
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
list_adapter.notifyChange(reason = "onClickEnqueteChoice", reset = true)
|
||||
} else if(result.error != null) {
|
||||
showToast(context, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun openFilterMenu(item : TootFilter) {
|
||||
val ad = ActionsDialog()
|
||||
ad.addAction(activity.getString(R.string.edit)) {
|
||||
@ -2604,12 +2886,14 @@ internal class ItemViewHolder(
|
||||
val b = Benchmark(log, "Item-Inflate", 40L)
|
||||
val rv = verticalLayout {
|
||||
// トップレベルのViewGroupのlparamsはイニシャライザ内部に置くしかないみたい
|
||||
layoutParams = androidx.recyclerview.widget.RecyclerView.LayoutParams(matchParent, wrapContent).apply {
|
||||
marginStart = dip(8)
|
||||
marginEnd = dip(8)
|
||||
topMargin = dip(2f)
|
||||
bottomMargin = dip(1f)
|
||||
}
|
||||
layoutParams =
|
||||
androidx.recyclerview.widget.RecyclerView.LayoutParams(matchParent, wrapContent)
|
||||
.apply {
|
||||
marginStart = dip(8)
|
||||
marginEnd = dip(8)
|
||||
topMargin = dip(2f)
|
||||
bottomMargin = dip(1f)
|
||||
}
|
||||
|
||||
setPaddingRelative(dip(4), dip(1f), dip(4), dip(2f))
|
||||
|
||||
|
@ -11,20 +11,19 @@ import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
enum class PollType {
|
||||
Mastodon, // Mastodon 2.8's poll
|
||||
Misskey, // Misskey's poll
|
||||
FriendsNico, // friends.nico
|
||||
}
|
||||
|
||||
class NicoEnquete(
|
||||
parser : TootParser,
|
||||
status : TootStatus,
|
||||
list_attachment : ArrayList<TootAttachmentLike>?,
|
||||
src : JSONObject
|
||||
src : JSONObject,
|
||||
val pollType : PollType
|
||||
) {
|
||||
enum class PollType{
|
||||
Mastodon, // Mastodon 2.8's poll
|
||||
Misskey, // Misskey's poll
|
||||
FriendsNico, // friends.nico
|
||||
}
|
||||
|
||||
val poll_type : PollType
|
||||
|
||||
// one of enquete,enquete_result
|
||||
val type : String?
|
||||
@ -37,10 +36,10 @@ class NicoEnquete(
|
||||
val items : ArrayList<Choice>?
|
||||
|
||||
// 結果の数値 // null or array of number
|
||||
private var ratios : MutableList<Float>?
|
||||
var ratios : MutableList<Float>? = null
|
||||
|
||||
// 結果の数値のテキスト // null or array of string
|
||||
private var ratios_text : MutableList<String>?
|
||||
var ratios_text : MutableList<String>? = null
|
||||
|
||||
var myVoted : Int? = null
|
||||
|
||||
@ -48,13 +47,20 @@ class NicoEnquete(
|
||||
val time_start : Long
|
||||
val status_id : EntityId
|
||||
|
||||
// Mastodon poll API
|
||||
var expired_at = Long.MAX_VALUE
|
||||
var expired = false
|
||||
var multiple = false
|
||||
var votes_count : Int? = null
|
||||
var maxVotesCount : Int? = null
|
||||
var pollId : EntityId? = null
|
||||
|
||||
init {
|
||||
|
||||
this.time_start = status.time_created_at
|
||||
this.status_id = status.id
|
||||
|
||||
if(parser.serviceType == ServiceType.MISSKEY) {
|
||||
this.poll_type = PollType.Misskey
|
||||
|
||||
this.items = parseChoiceListMisskey(
|
||||
|
||||
@ -65,7 +71,7 @@ class NicoEnquete(
|
||||
var votesMax = 1
|
||||
items?.forEachIndexed { index, choice ->
|
||||
if(choice.isVoted) this.myVoted = index
|
||||
val votes = choice.votes
|
||||
val votes = choice.votes ?: 0
|
||||
votesList.add(votes)
|
||||
if(votes > votesMax) votesMax = votes
|
||||
}
|
||||
@ -94,9 +100,54 @@ class NicoEnquete(
|
||||
emojiMapProfile = status.profile_emojis
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
} else if(pollType == PollType.Mastodon) {
|
||||
this.type = "enquete"
|
||||
|
||||
this.question = status.content
|
||||
this.decoded_question = DecodeOptions(
|
||||
parser.context,
|
||||
parser.linkHelper,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
attachmentList = list_attachment,
|
||||
linkTag = status,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
this.items = parseChoiceListMastodon(
|
||||
parser.context,
|
||||
status,
|
||||
src.optJSONArray("options")?.toObjectList()
|
||||
)
|
||||
|
||||
this.pollId = EntityId.mayNull(src.parseString("id"))
|
||||
this.expired_at = TootStatus.parseTime(src.parseString("expires_at")).notZero() ?: Long.MAX_VALUE
|
||||
this.expired = src.optBoolean("expired", false)
|
||||
this.multiple = src.optBoolean("multiple", false)
|
||||
this.votes_count = src.parseInt("votes_count")
|
||||
this.myVoted = if(src.optBoolean("voted", false)) 1 else null
|
||||
|
||||
if(this.items == null) {
|
||||
maxVotesCount = null
|
||||
} else if(this.multiple){
|
||||
var max :Int? = null
|
||||
for( item in items){
|
||||
val v = item.votes
|
||||
if( v != null && (max == null || v > max) ) max =v
|
||||
|
||||
}
|
||||
maxVotesCount = max
|
||||
} else {
|
||||
var sum :Int?= null
|
||||
for( item in items){
|
||||
val v = item.votes
|
||||
if( v != null ) sum = (sum?:0) + v
|
||||
}
|
||||
maxVotesCount = sum
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO Mastodonのpollとfriends.nicoのアンケートを区別する
|
||||
this.poll_type = PollType.FriendsNico
|
||||
this.type = src.parseString("type")
|
||||
|
||||
this.question = src.parseString("question")
|
||||
@ -111,14 +162,14 @@ class NicoEnquete(
|
||||
emojiMapProfile = status.profile_emojis
|
||||
).decodeHTML(this.question ?: "?")
|
||||
|
||||
this.items = parseChoiceList(
|
||||
this.items = parseChoiceListFriendsNico(
|
||||
parser.context,
|
||||
status,
|
||||
src.parseStringArrayList("items")
|
||||
)
|
||||
|
||||
this.ratios = src.parseFloatArrayList("ratios")
|
||||
this.ratios_text = src.parseStringArrayList( "ratios_text")
|
||||
this.ratios_text = src.parseStringArrayList("ratios_text")
|
||||
}
|
||||
|
||||
}
|
||||
@ -127,7 +178,8 @@ class NicoEnquete(
|
||||
val text : String,
|
||||
val decoded_text : Spannable,
|
||||
var isVoted : Boolean = false, // misskey
|
||||
var votes : Int = 0 // misskey
|
||||
var votes : Int? = 0, // misskey
|
||||
var checked : Boolean = false // Mastodon
|
||||
)
|
||||
|
||||
companion object {
|
||||
@ -147,27 +199,8 @@ class NicoEnquete(
|
||||
parser : TootParser,
|
||||
status : TootStatus,
|
||||
list_attachment : ArrayList<TootAttachmentLike>?,
|
||||
jsonString : String?
|
||||
) : NicoEnquete? {
|
||||
jsonString ?: return null
|
||||
return try {
|
||||
NicoEnquete(
|
||||
parser,
|
||||
status,
|
||||
list_attachment,
|
||||
jsonString.toJsonObject()
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun parse(
|
||||
parser : TootParser,
|
||||
status : TootStatus,
|
||||
list_attachment : ArrayList<TootAttachmentLike>?,
|
||||
src : JSONObject?
|
||||
src : JSONObject?,
|
||||
pollType : PollType
|
||||
) : NicoEnquete? {
|
||||
src ?: return null
|
||||
return try {
|
||||
@ -175,7 +208,8 @@ class NicoEnquete(
|
||||
parser,
|
||||
status,
|
||||
list_attachment,
|
||||
src
|
||||
src,
|
||||
pollType
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
@ -183,7 +217,40 @@ class NicoEnquete(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseChoiceList(
|
||||
private fun parseChoiceListMastodon(
|
||||
context : Context,
|
||||
status : TootStatus,
|
||||
objectArray : ArrayList<JSONObject>?
|
||||
) : ArrayList<Choice>? {
|
||||
if(objectArray != null) {
|
||||
val size = objectArray.size
|
||||
val items = ArrayList<Choice>(size)
|
||||
val options = DecodeOptions(
|
||||
context,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true
|
||||
)
|
||||
for(o in objectArray) {
|
||||
val text = reWhitespace
|
||||
.matcher((o.parseString("title") ?: "?").sanitizeBDI())
|
||||
.replaceAll(" ")
|
||||
val decoded_text = options.decodeHTML(text)
|
||||
|
||||
items.add(
|
||||
Choice(
|
||||
text,
|
||||
decoded_text,
|
||||
votes = o.parseInt("votes_count") // may null
|
||||
)
|
||||
)
|
||||
}
|
||||
if(items.isNotEmpty()) return items
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun parseChoiceListFriendsNico(
|
||||
context : Context,
|
||||
status : TootStatus,
|
||||
stringArray : ArrayList<String>?
|
||||
@ -243,13 +310,13 @@ class NicoEnquete(
|
||||
fun increaseVote(context : Context, argChoice : Int?, isMyVoted : Boolean) : Boolean {
|
||||
argChoice ?: return false
|
||||
|
||||
synchronized(this){
|
||||
synchronized(this) {
|
||||
try {
|
||||
// 既に投票済み状態なら何もしない
|
||||
if(myVoted != null) return false
|
||||
|
||||
val item = this.items?.get(argChoice) ?: return false
|
||||
item.votes += 1
|
||||
item.votes = (item.votes ?: 0) + 1
|
||||
if(isMyVoted) item.isVoted = true
|
||||
|
||||
// update ratios
|
||||
@ -257,7 +324,7 @@ class NicoEnquete(
|
||||
var votesMax = 1
|
||||
items.forEachIndexed { index, choice ->
|
||||
if(choice.isVoted) this.myVoted = index
|
||||
val votes = choice.votes
|
||||
val votes = choice.votes ?: 0
|
||||
votesList.add(votes)
|
||||
if(votes > votesMax) votesMax = votes
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
|
||||
// ページネーションには日時を使う
|
||||
this._orderId = EntityId(time_created_at.toString())
|
||||
|
||||
|
||||
// お気に入りカラムなどではパース直後に変更することがある
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
@ -323,7 +323,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
src.optJSONObject("poll")
|
||||
src.optJSONObject("poll"),
|
||||
PollType.Misskey
|
||||
)
|
||||
|
||||
this.reactionCounts = parseReactionCounts(src.optJSONObject("reactionCounts"))
|
||||
@ -385,8 +386,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
src.optJSONArray("media_attachments"),
|
||||
log
|
||||
)
|
||||
this.visibility = TootVisibility.parseMastodon(src.parseString("visibility")) ?:
|
||||
TootVisibility.Public
|
||||
this.visibility = TootVisibility.parseMastodon(src.parseString("visibility"))
|
||||
?: TootVisibility.Public
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
}
|
||||
@ -432,7 +433,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
|
||||
this._orderId = this.id
|
||||
this.in_reply_to_id = EntityId.mayNull(src.parseString("in_reply_to_id"))
|
||||
this.in_reply_to_account_id = EntityId.mayNull(src.parseString("in_reply_to_account_id"))
|
||||
this.in_reply_to_account_id =
|
||||
EntityId.mayNull(src.parseString("in_reply_to_account_id"))
|
||||
this.mentions = parseListOrNull(::TootMention, src.optJSONArray("mentions"), log)
|
||||
this.tags = parseListOrNull(::TootTag, src.optJSONArray("tags"))
|
||||
this.application =
|
||||
@ -467,7 +469,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
val sv = (src.parseString("spoiler_text") ?: "").cleanCW()
|
||||
var sv = (src.parseString("spoiler_text") ?: "").cleanCW()
|
||||
this.spoiler_text = when {
|
||||
sv.isEmpty() -> "" // CWなし
|
||||
sv.isBlank() -> parser.context.getString(R.string.blank_cw)
|
||||
@ -488,12 +490,30 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
this.enquete = NicoEnquete.parse(
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
src.parseString("enquete")
|
||||
)
|
||||
this.enquete = try {
|
||||
sv = src.parseString("enquete") ?: ""
|
||||
if(sv.isNotEmpty()) {
|
||||
NicoEnquete.parse(
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
sv.toJsonObject(),
|
||||
PollType.FriendsNico
|
||||
)
|
||||
} else {
|
||||
val ov = src.optJSONObject("poll")
|
||||
NicoEnquete.parse(
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
ov,
|
||||
PollType.Mastodon
|
||||
)
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
null
|
||||
}
|
||||
|
||||
// Pinned TL を取得した時にreblogが登場することはないので、reblogについてpinned 状態を気にする必要はない
|
||||
this.reblog = parser.status(src.optJSONObject("reblog"))
|
||||
@ -664,7 +684,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
|
||||
fun markDeleted(context : Context, deletedAt : Long?) : Boolean? {
|
||||
|
||||
if( Pref.bpDontRemoveDeletedToot( App1.getAppState(context).pref)) return false
|
||||
if(Pref.bpDontRemoveDeletedToot(App1.getAppState(context).pref)) return false
|
||||
|
||||
var sv = if(deletedAt != null) {
|
||||
context.getString(R.string.status_deleted_at, formatTime(context, deletedAt, false))
|
||||
@ -717,7 +737,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
"""\Ahttps://([^/]+)/notes/([0-9a-f]{24})\b""",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
)
|
||||
|
||||
|
||||
// PleromaのStatusのUri
|
||||
internal val reStatusPageObjects =
|
||||
Pattern.compile("""\Ahttps://([^/]+)/objects/([^?#/\s]+)(?:\z|[?#])""")
|
||||
@ -919,10 +939,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
return if(host != null && host.isNotEmpty() && host != "?") host else null
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun validStatusId(src : EntityId?) : EntityId? =
|
||||
when{
|
||||
when {
|
||||
src == null -> null
|
||||
src == EntityId.DEFAULT -> null
|
||||
src.toString().startsWith("-") -> null
|
||||
@ -951,7 +969,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
|
||||
// https://misskey.xyz/notes/5b802367744b650030a13640
|
||||
m = reStatusPageMisskey.matcher(uri)
|
||||
if( m.find()) return EntityId(m.group(2))
|
||||
if(m.find()) return EntityId(m.group(2))
|
||||
|
||||
// https://pl.at7s.me/objects/feeb4399-cd7a-48c8-8999-b58868daaf43
|
||||
// tootsearch中の投稿からIDを読めるようにしたい
|
||||
@ -982,8 +1000,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
||||
|
||||
// https://misskey.xyz/notes/5b802367744b650030a13640
|
||||
m = reStatusPageMisskey.matcher(url)
|
||||
if( m.find()) return EntityId(m.group(2))
|
||||
|
||||
if(m.find()) return EntityId(m.group(2))
|
||||
|
||||
// https://pl.at7s.me/objects/feeb4399-cd7a-48c8-8999-b58868daaf43
|
||||
// tootsearch中の投稿からIDを読めるようにしたい
|
||||
// しかしこのURL中のuuidはステータスIDではないので、無意味
|
||||
|
@ -0,0 +1,42 @@
|
||||
package jp.juggler.subwaytooter.drawable
|
||||
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
class PollPlotDrawable(
|
||||
private val color :Int,
|
||||
private val startWidth: Int, // pixel width for minimum gauge
|
||||
private val ratio: Float, // gauge ratio in 0..1
|
||||
private val isRtl :Boolean = false // false for LTR, true for RTL layout
|
||||
) : Drawable(){
|
||||
|
||||
override fun setAlpha(alpha : Int) {
|
||||
}
|
||||
|
||||
override fun getOpacity() : Int = PixelFormat.TRANSLUCENT
|
||||
|
||||
override fun setColorFilter(colorFilter : ColorFilter?) {
|
||||
}
|
||||
|
||||
private val rect = Rect()
|
||||
private val paint = Paint()
|
||||
|
||||
override fun draw(canvas : Canvas) {
|
||||
|
||||
val bounds = bounds
|
||||
val w = bounds.width()
|
||||
val ratioWidth = ( (w - startWidth) * ratio + 0.5f ).toInt()
|
||||
|
||||
val remainWidth = w - ratioWidth - startWidth
|
||||
|
||||
if( isRtl){
|
||||
rect.set(bounds.left+remainWidth,bounds.top,bounds.right,bounds.bottom)
|
||||
}else{
|
||||
rect.set(bounds.left,bounds.top,bounds.right-remainWidth,bounds.bottom)
|
||||
}
|
||||
paint.color = color
|
||||
canvas.drawRect(rect,paint)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -71,7 +71,7 @@ class PostHelper(
|
||||
var in_reply_to_id : EntityId? = null
|
||||
var attachment_list : ArrayList<PostAttachment>? = null
|
||||
var enquete_items : ArrayList<String>? = null
|
||||
var poll_type : NicoEnquete.PollType? = null
|
||||
var poll_type : PollType? = null
|
||||
var poll_expire_seconds = 0
|
||||
var poll_hide_totals = false
|
||||
var poll_multiple_choice = false
|
||||
@ -126,7 +126,7 @@ class PostHelper(
|
||||
val choice_max_chars = if(isMisskey) {
|
||||
15
|
||||
} else when(poll_type) {
|
||||
NicoEnquete.PollType.Mastodon -> 25
|
||||
PollType.Mastodon -> 25
|
||||
else -> 15
|
||||
}
|
||||
|
||||
@ -496,7 +496,7 @@ class PostHelper(
|
||||
}
|
||||
|
||||
if(enquete_items?.isNotEmpty() == true) {
|
||||
if(poll_type == NicoEnquete.PollType.Mastodon) {
|
||||
if(poll_type == PollType.Mastodon) {
|
||||
json.put("poll", JSONObject().apply {
|
||||
put("multiple", poll_multiple_choice)
|
||||
put("hide_totals", poll_hide_totals)
|
||||
|
@ -610,7 +610,7 @@
|
||||
<CheckBox
|
||||
android:id="@+id/cbNotificationVote"
|
||||
style="@style/setting_horizontal_stretch"
|
||||
android:text="@string/vote"
|
||||
android:text="@string/vote_misskey"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
@ -415,7 +415,7 @@
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label_indent1"
|
||||
android:text="@string/vote"
|
||||
android:text="@string/vote_misskey"
|
||||
/>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
@ -565,7 +565,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="0dp"
|
||||
android:background="@drawable/btn_bg_transparent"
|
||||
android:contentDescription="@string/vote"
|
||||
android:contentDescription="@string/vote_misskey"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
@ -748,7 +748,7 @@
|
||||
<string name="follow_request_cancelled">Follow request was cancelled.</string>
|
||||
<string name="confirm_cancel_follow_request_who_from">La demande de suivi de %2$s à %1$s sera rejetée. Vous êtes sûr ?</string>
|
||||
<string name="reaction">Réaction (Misskey)</string>
|
||||
<string name="vote">Vote (Misskey)</string>
|
||||
<string name="vote_misskey">Vote (Misskey)</string>
|
||||
<string name="timeout_for_embed_media_viewer">Timeout for embed media viewer (unit:seconds, app restart(delete from app history) required)</string>
|
||||
<string name="media_attachment_max_byte_size_movie">Taille maximale en octets des médias vidéo (unité : méga octets. par défaut : 40)</string>
|
||||
<string name="link_color">Couleur des liens (redémarrage requis)</string>
|
||||
|
@ -749,7 +749,7 @@
|
||||
<string name="visibility_local_followers">フォロワー (ローカル)</string>
|
||||
<string name="visibility_local_unlisted">未収載 (ローカル)</string>
|
||||
|
||||
<string name="vote">投票 (Misskey)</string>
|
||||
<string name="vote_misskey">投票 (Misskey)</string>
|
||||
<string name="vote_count_text">%1$d票</string>
|
||||
<string name="wait_previous_operation">直前の操作が完了するまでお待ちください</string>
|
||||
<string name="with_attachment">添付データあり</string>
|
||||
@ -879,5 +879,10 @@
|
||||
<string name="poll_expire_days">日</string>
|
||||
<string name="poll_expire_hours">時間</string>
|
||||
<string name="poll_expire_minutes">分</string>
|
||||
<string name="vote_1">1 vote</string>
|
||||
<string name="vote_2">%1$d votes</string>
|
||||
<string name="vote_expire_at">投票期限 %1$s</string>
|
||||
<string name="vote_count_unavailable">\?\?\?票</string>
|
||||
<string name="vote_button">投票</string>
|
||||
|
||||
</resources>
|
||||
|
@ -769,7 +769,7 @@
|
||||
<string name="follow_request_cancelled">팔로우 요청 취소됨.</string>
|
||||
<string name="confirm_cancel_follow_request_who_from">%2$s로부터 %1$s로의 팔로우 요청을 취소할까요\?</string>
|
||||
<string name="reaction">반응 (Misskey)</string>
|
||||
<string name="vote">투표 (Misskey)</string>
|
||||
<string name="vote_misskey">투표 (Misskey)</string>
|
||||
<string name="timeout_for_embed_media_viewer">내장 미디어 뷰어 시간제한 (단위:초, 앱 재시작(앱 사용이력에서 삭제) 필요)</string>
|
||||
<string name="link_color">링크 색 (앱 재시작 필요)</string>
|
||||
<string name="missing_closeable_column">닫을 칼럼이 표시 범위에 없음.</string>
|
||||
|
@ -719,7 +719,7 @@
|
||||
<string name="follow_request_cancelled">Følgingsforespørsel forkastet.</string>
|
||||
<string name="confirm_cancel_follow_request_who_from">Følgingsforespørsel fra %1$s til %2$s vil forkastes. Er du sikker\?</string>
|
||||
<string name="reaction">Reaksjon (Misskey)</string>
|
||||
<string name="vote">Stem (Misskey)</string>
|
||||
<string name="vote_misskey">Stem (Misskey)</string>
|
||||
<string name="timeout_for_embed_media_viewer">Tidsavbrudd for innebygd mediaviser (enhet:sekunder, programomstart (sletting fra programhistorikk) kreves)</string>
|
||||
<string name="link_color">Lenkefarge (programomstart kreves)</string>
|
||||
<string name="missing_closeable_column">mangler lukkbar kolonne i synlig område.</string>
|
||||
|
@ -778,7 +778,7 @@
|
||||
<string name="follow_request_cancelled">Follow request cancelled.</string>
|
||||
<string name="confirm_cancel_follow_request_who_from">Cancel follow request from %2$s to %1$s?</string>
|
||||
<string name="reaction">Reaction (Misskey)</string>
|
||||
<string name="vote">Vote (Misskey)</string>
|
||||
<string name="vote_misskey">Vote (Misskey)</string>
|
||||
<string name="timeout_for_embed_media_viewer">Timeout for embedded media viewer (Unit:seconds, app restart(delete from app history) required)</string>
|
||||
<string name="link_color">Link color (app restart required)</string>
|
||||
<string name="missing_closeable_column">Missing closeable column in visible range.</string>
|
||||
@ -902,5 +902,10 @@
|
||||
<string name="poll_expire_days">days</string>
|
||||
<string name="poll_expire_hours">hours</string>
|
||||
<string name="poll_expire_minutes">minutes</string>
|
||||
<string name="vote_1">1 vote</string>
|
||||
<string name="vote_2">%1$d votes</string>
|
||||
<string name="vote_expire_at">time limit: %1$s</string>
|
||||
<string name="vote_count_unavailable">\?\?\? votes</string>
|
||||
<string name="vote_button">Vote</string>
|
||||
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user