Poll fixes (#1238)

* update cache when voting on a poll

* fix poll controls color

* don't allow voting on old poll from cache

* check for RecyclerView.NO_POSITION in click listener

* fix crash when voting in a boosted poll
This commit is contained in:
Konrad Pozniak 2019-05-05 08:26:17 +02:00 committed by GitHub
parent fae9cd18f1
commit b8c32a96de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 31 deletions

View File

@ -775,9 +775,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
pollButton.setVisibility(View.GONE); pollButton.setVisibility(View.GONE);
} else { } else {
long timestamp = System.currentTimeMillis();
boolean expired = poll.getExpired() || (poll.getExpiresAt() != null && timestamp > poll.getExpiresAt().getTime());
Context context = pollDescription.getContext(); Context context = pollDescription.getContext();
if(poll.getExpired() || poll.getVoted()) { if(expired || poll.getVoted()) {
// no voting possible // no voting possible
setupPollResult(poll, emojis); setupPollResult(poll, emojis);
} else { } else {
@ -790,14 +794,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
String votes = numberFormat.format(poll.getVotesCount()); String votes = numberFormat.format(poll.getVotesCount());
String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes); String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes);
CharSequence pollDurationInfo; CharSequence pollDurationInfo;
if(poll.getExpired()) { if(expired) {
pollDurationInfo = context.getString(R.string.poll_info_closed); pollDurationInfo = context.getString(R.string.poll_info_closed);
} else { } else {
if(useAbsoluteTime) { if(useAbsoluteTime) {
pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.getExpiresAt())); pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.getExpiresAt()));
} else { } else {
String pollDuration = DateUtils.formatDuration(pollDescription.getContext(), poll.getExpiresAt().getTime(), System.currentTimeMillis()); String pollDuration = DateUtils.formatDuration(pollDescription.getContext(), poll.getExpiresAt().getTime(), timestamp);
pollDurationInfo = context.getString(R.string.poll_info_time_relative, pollDuration); pollDurationInfo = context.getString(R.string.poll_info_time_relative, pollDuration);
} }
} }
@ -864,17 +869,23 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
pollButton.setOnClickListener(v -> { pollButton.setOnClickListener(v -> {
List<Integer> pollResult = new ArrayList<>(options.size()); int position = getAdapterPosition();
for(int i = 0; i < options.size(); i++) {
if(pollCheckboxOptions[i].isChecked()) { if (position != RecyclerView.NO_POSITION) {
pollResult.add(i);
List<Integer> pollResult = new ArrayList<>(options.size());
for (int i = 0; i < options.size(); i++) {
if (pollCheckboxOptions[i].isChecked()) {
pollResult.add(i);
}
} }
} if (pollResult.size() == 0) {
if(pollResult.size() == 0) { return;
return; }
listener.onVoteInPoll(position, pollResult);
} }
listener.onVoteInPoll(getAdapterPosition(), pollResult);
}); });
} else { } else {
@ -896,25 +907,30 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
pollButton.setOnClickListener(v -> { pollButton.setOnClickListener(v -> {
int selectedRadioButtonIndex; int position = getAdapterPosition();
switch (pollRadioGroup.getCheckedRadioButtonId()) {
case R.id.status_poll_radio_button_0:
selectedRadioButtonIndex = 0;
break;
case R.id.status_poll_radio_button_1:
selectedRadioButtonIndex = 1;
break;
case R.id.status_poll_radio_button_2:
selectedRadioButtonIndex = 2;
break;
case R.id.status_poll_radio_button_3:
selectedRadioButtonIndex = 3;
break;
default:
return;
}
listener.onVoteInPoll(getAdapterPosition(), Collections.singletonList(selectedRadioButtonIndex)); if (position != RecyclerView.NO_POSITION) {
int selectedRadioButtonIndex;
switch (pollRadioGroup.getCheckedRadioButtonId()) {
case R.id.status_poll_radio_button_0:
selectedRadioButtonIndex = 0;
break;
case R.id.status_poll_radio_button_1:
selectedRadioButtonIndex = 1;
break;
case R.id.status_poll_radio_button_2:
selectedRadioButtonIndex = 2;
break;
case R.id.status_poll_radio_button_3:
selectedRadioButtonIndex = 3;
break;
default:
return;
}
listener.onVoteInPoll(position, Collections.singletonList(selectedRadioButtonIndex));
}
}); });
} }

View File

@ -1,5 +1,6 @@
package com.keylesspalace.tusky.appstore package com.keylesspalace.tusky.appstore
import com.google.gson.Gson
import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.db.AppDatabase
import io.reactivex.Single import io.reactivex.Single
@ -10,7 +11,8 @@ import javax.inject.Inject
class CacheUpdater @Inject constructor( class CacheUpdater @Inject constructor(
eventHub: EventHub, eventHub: EventHub,
accountManager: AccountManager, accountManager: AccountManager,
private val appDatabase: AppDatabase private val appDatabase: AppDatabase,
gson: Gson
) { ) {
private val disposable: Disposable private val disposable: Disposable
@ -28,6 +30,10 @@ class CacheUpdater @Inject constructor(
timelineDao.removeAllByUser(accountId, event.accountId) timelineDao.removeAllByUser(accountId, event.accountId)
is StatusDeletedEvent -> is StatusDeletedEvent ->
timelineDao.delete(accountId, event.statusId) timelineDao.delete(accountId, event.statusId)
is PollVoteEvent -> {
val pollString = gson.toJson(event.poll)
timelineDao.setVoted(accountId, event.statusId, pollString)
}
} }
} }
} }

View File

@ -6,6 +6,7 @@ import androidx.room.OnConflictStrategy.IGNORE
import androidx.room.OnConflictStrategy.REPLACE import androidx.room.OnConflictStrategy.REPLACE
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction import androidx.room.Transaction
import com.keylesspalace.tusky.entity.Poll
import io.reactivex.Single import io.reactivex.Single
@Dao @Dao
@ -98,4 +99,8 @@ AND serverId = :statusId""")
@Query("""DELETE FROM TimelineStatusEntity WHERE timelineUserId = :accountId @Query("""DELETE FROM TimelineStatusEntity WHERE timelineUserId = :accountId
AND authorServerId != :accountServerId AND createdAt < :olderThan""") AND authorServerId != :accountServerId AND createdAt < :olderThan""")
abstract fun cleanup(accountId: Long, accountServerId: String, olderThan: Long) abstract fun cleanup(accountId: Long, accountServerId: String, olderThan: Long)
@Query("""UPDATE TimelineStatusEntity SET poll = :poll
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId - :statusId)""")
abstract fun setVoted(accountId: Long, statusId: String, poll: String)
} }

View File

@ -622,9 +622,12 @@ public class TimelineFragment extends SFragment implements
} }
public void onVoteInPoll(int position, @NonNull List<Integer> choices) { public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
final Status status = statuses.get(position).asRight(); final Status status = statuses.get(position).asRight();
setVoteForPoll(position, status, status.getPoll().votedCopy(choices)); Poll votedPoll = status.getActionableStatus().getPoll().votedCopy(choices);
setVoteForPoll(position, status, votedPoll);
timelineCases.voteInPoll(status, choices) timelineCases.voteInPoll(status, choices)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -431,6 +431,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 1" /> tools:text="Option 1" />
<RadioButton <RadioButton
@ -438,6 +439,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 2" /> tools:text="Option 2" />
<RadioButton <RadioButton
@ -445,6 +447,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 3" /> tools:text="Option 3" />
<RadioButton <RadioButton
@ -452,6 +455,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 4" /> tools:text="Option 4" />
</RadioGroup> </RadioGroup>
@ -461,6 +465,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
@ -472,6 +477,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
@ -483,6 +489,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
@ -494,6 +501,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2"

View File

@ -417,6 +417,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 1" /> tools:text="Option 1" />
<RadioButton <RadioButton
@ -424,6 +425,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 2" /> tools:text="Option 2" />
<RadioButton <RadioButton
@ -431,6 +433,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 3" /> tools:text="Option 3" />
<RadioButton <RadioButton
@ -438,6 +441,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 4" /> tools:text="Option 4" />
</RadioGroup> </RadioGroup>
@ -447,6 +451,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
@ -458,6 +463,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
@ -469,6 +475,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
@ -480,6 +487,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2"

View File

@ -426,6 +426,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 1" /> tools:text="Option 1" />
<RadioButton <RadioButton
@ -433,6 +434,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 2" /> tools:text="Option 2" />
<RadioButton <RadioButton
@ -440,6 +442,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 3" /> tools:text="Option 3" />
<RadioButton <RadioButton
@ -447,6 +450,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:buttonTint="?attr/compound_button_color"
tools:text="Option 4" /> tools:text="Option 4" />
</RadioGroup> </RadioGroup>
@ -456,6 +460,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
@ -467,6 +472,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
@ -478,6 +484,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
@ -489,6 +496,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2"