diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 4c5aceff4..9a5b03479 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -775,9 +775,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { pollButton.setVisibility(View.GONE); } else { + long timestamp = System.currentTimeMillis(); + + boolean expired = poll.getExpired() || (poll.getExpiresAt() != null && timestamp > poll.getExpiresAt().getTime()); + Context context = pollDescription.getContext(); - if(poll.getExpired() || poll.getVoted()) { + if(expired || poll.getVoted()) { // no voting possible setupPollResult(poll, emojis); } else { @@ -790,14 +794,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { String votes = numberFormat.format(poll.getVotesCount()); String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes); + CharSequence pollDurationInfo; - if(poll.getExpired()) { + if(expired) { pollDurationInfo = context.getString(R.string.poll_info_closed); } else { if(useAbsoluteTime) { pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.getExpiresAt())); } 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); } } @@ -864,17 +869,23 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { pollButton.setOnClickListener(v -> { - List pollResult = new ArrayList<>(options.size()); - for(int i = 0; i < options.size(); i++) { - if(pollCheckboxOptions[i].isChecked()) { - pollResult.add(i); + int position = getAdapterPosition(); + + if (position != RecyclerView.NO_POSITION) { + + List pollResult = new ArrayList<>(options.size()); + for (int i = 0; i < options.size(); i++) { + if (pollCheckboxOptions[i].isChecked()) { + pollResult.add(i); + } } - } - if(pollResult.size() == 0) { - return; + if (pollResult.size() == 0) { + return; + } + + listener.onVoteInPoll(position, pollResult); } - listener.onVoteInPoll(getAdapterPosition(), pollResult); }); } else { @@ -896,25 +907,30 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { pollButton.setOnClickListener(v -> { - 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; - } + int position = getAdapterPosition(); - 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)); + } }); } diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt index 0e5cd3fa7..6d3f9733a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt @@ -1,5 +1,6 @@ package com.keylesspalace.tusky.appstore +import com.google.gson.Gson import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase import io.reactivex.Single @@ -10,7 +11,8 @@ import javax.inject.Inject class CacheUpdater @Inject constructor( eventHub: EventHub, accountManager: AccountManager, - private val appDatabase: AppDatabase + private val appDatabase: AppDatabase, + gson: Gson ) { private val disposable: Disposable @@ -28,6 +30,10 @@ class CacheUpdater @Inject constructor( timelineDao.removeAllByUser(accountId, event.accountId) is StatusDeletedEvent -> timelineDao.delete(accountId, event.statusId) + is PollVoteEvent -> { + val pollString = gson.toJson(event.poll) + timelineDao.setVoted(accountId, event.statusId, pollString) + } } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index f8e27cbda..ce5e000ed 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -6,6 +6,7 @@ import androidx.room.OnConflictStrategy.IGNORE import androidx.room.OnConflictStrategy.REPLACE import androidx.room.Query import androidx.room.Transaction +import com.keylesspalace.tusky.entity.Poll import io.reactivex.Single @Dao @@ -98,4 +99,8 @@ AND serverId = :statusId""") @Query("""DELETE FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND authorServerId != :accountServerId AND createdAt < :olderThan""") 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) } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index 57b578216..5fbd5afb9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -622,9 +622,12 @@ public class TimelineFragment extends SFragment implements } public void onVoteInPoll(int position, @NonNull List choices) { + 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) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/res/layout/item_conversation.xml b/app/src/main/res/layout/item_conversation.xml index 5274889c3..c7d33225a 100644 --- a/app/src/main/res/layout/item_conversation.xml +++ b/app/src/main/res/layout/item_conversation.xml @@ -431,6 +431,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="?attr/status_text_medium" + app:buttonTint="?attr/compound_button_color" tools:text="Option 1" /> @@ -461,6 +465,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" @@ -472,6 +477,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" @@ -483,6 +489,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" @@ -494,6 +501,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml index ac2a70b66..4faef75d4 100644 --- a/app/src/main/res/layout/item_status.xml +++ b/app/src/main/res/layout/item_status.xml @@ -417,6 +417,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="?attr/status_text_medium" + app:buttonTint="?attr/compound_button_color" tools:text="Option 1" /> @@ -447,6 +451,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" @@ -458,6 +463,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" @@ -469,6 +475,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" @@ -480,6 +487,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" diff --git a/app/src/main/res/layout/item_status_detailed.xml b/app/src/main/res/layout/item_status_detailed.xml index 1b7c79ed0..e9269fe05 100644 --- a/app/src/main/res/layout/item_status_detailed.xml +++ b/app/src/main/res/layout/item_status_detailed.xml @@ -426,6 +426,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="?attr/status_text_medium" + app:buttonTint="?attr/compound_button_color" tools:text="Option 1" /> @@ -456,6 +460,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group" @@ -467,6 +472,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0" @@ -478,6 +484,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1" @@ -489,6 +496,7 @@ android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" + app:buttonTint="?attr/compound_button_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2"