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 311b05005..3117b3a94 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -678,7 +678,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { showConfirmReblog(listener, buttonState, position); return false; } else { - listener.onReblog(!buttonState, position); + listener.onReblog(!buttonState, position, Status.Visibility.PUBLIC); return true; } } else { @@ -739,13 +739,25 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { popup.inflate(R.menu.status_reblog); Menu menu = popup.getMenu(); if (buttonState) { - menu.findItem(R.id.menu_action_reblog).setVisible(false); + menu.setGroupVisible(R.id.menu_action_reblog_group, false); } else { menu.findItem(R.id.menu_action_unreblog).setVisible(false); } popup.setOnMenuItemClickListener(item -> { - listener.onReblog(!buttonState, position); - if (!buttonState) { + if (buttonState) { + listener.onReblog(false, position, Status.Visibility.PUBLIC); + } else { + Status.Visibility visibility; + if (item.getItemId() == R.id.menu_action_reblog_public) { + visibility = Status.Visibility.PUBLIC; + } else if (item.getItemId() == R.id.menu_action_reblog_unlisted) { + visibility = Status.Visibility.UNLISTED; + } else if (item.getItemId() == R.id.menu_action_reblog_private) { + visibility = Status.Visibility.PRIVATE; + } else { + visibility = Status.Visibility.PUBLIC; + } + listener.onReblog(true, position, visibility); reblogButton.playAnimation(); reblogButton.setChecked(true); } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 5cfd1d7b3..9f8cd1018 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -41,6 +41,7 @@ import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.components.account.AccountActivity import com.keylesspalace.tusky.databinding.FragmentTimelineBinding +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.interfaces.StatusActionListener @@ -231,7 +232,7 @@ class ConversationsFragment : adapter?.refresh() } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { // its impossible to reblog private messages } @@ -335,7 +336,7 @@ class ConversationsFragment : } } - override fun onVoteInPoll(position: Int, choices: MutableList) { + override fun onVoteInPoll(position: Int, choices: List) { adapter?.peek(position)?.let { conversation -> viewModel.voteInPoll(choices, conversation) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt index c4909e920..a24eb35eb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt @@ -55,6 +55,7 @@ import com.keylesspalace.tusky.components.systemnotifications.NotificationChanne import com.keylesspalace.tusky.components.systemnotifications.NotificationService import com.keylesspalace.tusky.databinding.FragmentTimelineNotificationsBinding import com.keylesspalace.tusky.databinding.NotificationsFilterBinding +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.interfaces.AccountActionListener import com.keylesspalace.tusky.interfaces.ReselectableFragment @@ -335,9 +336,9 @@ class NotificationsFragment : viewModel.remove(notification.id) } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { val status = notificationsAdapter?.peek(position)?.asStatusOrNull() ?: return - viewModel.reblog(reblog, status) + viewModel.reblog(reblog, status, visibility) } override val onMoreTranslate: (translate: Boolean, position: Int) -> Unit diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt index 254e44722..0a61857cf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt @@ -45,6 +45,7 @@ import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.db.entity.NotificationPolicyEntity import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Notification +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.settings.PrefKeys @@ -204,8 +205,8 @@ class NotificationsViewModel @Inject constructor( } } - fun reblog(reblog: Boolean, status: StatusViewData.Concrete): Job = viewModelScope.launch { - timelineCases.reblog(status.actionableId, reblog).onFailure { t -> + fun reblog(reblog: Boolean, status: StatusViewData.Concrete, visibility: Status.Visibility = Status.Visibility.PUBLIC): Job = viewModelScope.launch { + timelineCases.reblog(status.actionableId, reblog, visibility).onFailure { t -> ifExpected(t) { Log.w(TAG, "Failed to reblog status " + status.actionableId, t) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsFragment.kt index 5ddc93da9..30eee5747 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsFragment.kt @@ -34,6 +34,7 @@ import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.notifications.NotificationActionListener import com.keylesspalace.tusky.components.notifications.NotificationsPagingAdapter import com.keylesspalace.tusky.databinding.FragmentNotificationRequestDetailsBinding +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.interfaces.AccountActionListener import com.keylesspalace.tusky.interfaces.StatusActionListener @@ -160,9 +161,9 @@ class NotificationRequestDetailsFragment : SFragment(R.layout.fragment_notificat viewModel.remove(notification) } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { val status = adapter?.peek(position)?.asStatusOrNull() ?: return - viewModel.reblog(reblog, status) + viewModel.reblog(reblog, status, visibility) } override val onMoreTranslate: ((Boolean, Int) -> Unit)? diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsViewModel.kt index 521bd0a5a..c06c1c726 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/requests/details/NotificationRequestDetailsViewModel.kt @@ -158,8 +158,8 @@ class NotificationRequestDetailsViewModel @AssistedInject constructor( currentSource?.invalidate() } - fun reblog(reblog: Boolean, status: StatusViewData.Concrete) = viewModelScope.launch { - timelineCases.reblog(status.actionableId, reblog).onFailure { t -> + fun reblog(reblog: Boolean, status: StatusViewData.Concrete, visibility: Status.Visibility = Status.Visibility.PUBLIC) = viewModelScope.launch { + timelineCases.reblog(status.actionableId, reblog, visibility).onFailure { t -> ifExpected(t) { Log.w(TAG, "Failed to reblog status " + status.actionableId, t) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index 004f120dc..09bda62a1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -139,9 +139,9 @@ class SearchViewModel @Inject constructor( updateStatusViewData(statusViewData.copy(isExpanded = expanded)) } - fun reblog(statusViewData: StatusViewData.Concrete, reblog: Boolean) { + fun reblog(statusViewData: StatusViewData.Concrete, reblog: Boolean, visibility: Status.Visibility = Status.Visibility.PUBLIC) { viewModelScope.launch { - timelineCases.reblog(statusViewData.id, reblog).fold({ + timelineCases.reblog(statusViewData.id, reblog, visibility).fold({ updateStatus( statusViewData.status.copy( reblogged = reblog, @@ -162,7 +162,7 @@ class SearchViewModel @Inject constructor( updateStatusViewData(statusViewData.copy(isCollapsed = collapsed)) } - fun voteInPoll(statusViewData: StatusViewData.Concrete, choices: MutableList) { + fun voteInPoll(statusViewData: StatusViewData.Concrete, choices: List) { val votedPoll = statusViewData.status.actionableStatus.poll!!.votedCopy(choices) updateStatus(statusViewData.status.copy(poll = votedPoll)) viewModelScope.launch { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index 25b7285f6..525f4e9dc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -251,7 +251,7 @@ class SearchStatusesFragment : SearchFragment(), Status } } - override fun onVoteInPoll(position: Int, choices: MutableList) { + override fun onVoteInPoll(position: Int, choices: List) { adapter?.peek(position)?.let { viewModel.voteInPoll(it, choices) } @@ -265,9 +265,9 @@ class SearchStatusesFragment : SearchFragment(), Status } } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { adapter?.peek(position)?.let { status -> - viewModel.reblog(status, reblog) + viewModel.reblog(status, reblog, visibility) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt index fba5b725b..4029dd67b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt @@ -415,9 +415,9 @@ class TimelineFragment : super.reply(status.status) } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { val status = adapter?.peek(position)?.asStatusOrNull() ?: return - viewModel.reblog(reblog, status) + viewModel.reblog(reblog, status, visibility) } private fun onTranslate(position: Int) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt index 4dc58e99f..4de9f88ca 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt @@ -29,6 +29,7 @@ import com.keylesspalace.tusky.components.preference.PreferencesFragment.Reading import com.keylesspalace.tusky.components.timeline.util.ifExpected import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.entity.Filter +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.usecase.TimelineCases @@ -107,9 +108,9 @@ abstract class TimelineViewModel( } } - fun reblog(reblog: Boolean, status: StatusViewData.Concrete): Job = viewModelScope.launch { + fun reblog(reblog: Boolean, status: StatusViewData.Concrete, visibility: Status.Visibility = Status.Visibility.PUBLIC): Job = viewModelScope.launch { try { - timelineCases.reblog(status.actionableId, reblog).getOrThrow() + timelineCases.reblog(status.actionableId, reblog, visibility).getOrThrow() } catch (t: Exception) { ifExpected(t) { Log.d(TAG, "Failed to reblog status " + status.actionableId, t) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt index 7c27491d3..90ade920d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt @@ -40,6 +40,7 @@ import com.keylesspalace.tusky.components.accountlist.AccountListActivity import com.keylesspalace.tusky.components.accountlist.AccountListActivity.Companion.newIntent import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsFragment import com.keylesspalace.tusky.databinding.FragmentViewThreadBinding +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.interfaces.StatusActionListener import com.keylesspalace.tusky.settings.PrefKeys @@ -324,9 +325,9 @@ class ViewThreadFragment : super.reply(viewData.status) } - override fun onReblog(reblog: Boolean, position: Int) { + override fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) { val status = adapter?.currentList?.getOrNull(position) ?: return - viewModel.reblog(reblog, status) + viewModel.reblog(reblog, status, visibility) } override val onMoreTranslate: ((translate: Boolean, position: Int) -> Unit) = diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt index f32f2c3a2..d51275e19 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt @@ -196,9 +196,9 @@ class ViewThreadViewModel @Inject constructor( } } - fun reblog(reblog: Boolean, status: StatusViewData.Concrete): Job = viewModelScope.launch { + fun reblog(reblog: Boolean, status: StatusViewData.Concrete, visibility: Status.Visibility = Status.Visibility.PUBLIC): Job = viewModelScope.launch { try { - timelineCases.reblog(status.actionableId, reblog).getOrThrow() + timelineCases.reblog(status.actionableId, reblog, visibility).getOrThrow() } catch (t: Exception) { ifExpected(t) { Log.d(TAG, "Failed to reblog status " + status.actionableId, t) diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt index 99263eb78..ca6ca4c89 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt @@ -74,7 +74,7 @@ import kotlinx.coroutines.launch * up what needs to be where. */ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId) { protected abstract fun removeItem(position: Int) - protected abstract fun onReblog(reblog: Boolean, position: Int) + protected abstract fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility) /** `null` if translation is not supported on this screen */ protected abstract val onMoreTranslate: ((translate: Boolean, position: Int) -> Unit)? @@ -318,12 +318,12 @@ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayo } R.id.status_unreblog_private -> { - onReblog(false, position) + onReblog(false, position, Status.Visibility.PUBLIC) return@setOnMenuItemClickListener true } R.id.status_reblog_private -> { - onReblog(true, position) + onReblog(true, position, Status.Visibility.PUBLIC) return@setOnMenuItemClickListener true } diff --git a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.kt similarity index 50% rename from app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.kt index 75f6ed749..99503576f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.kt @@ -12,60 +12,55 @@ * * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ +package com.keylesspalace.tusky.interfaces -package com.keylesspalace.tusky.interfaces; +import android.view.View +import com.keylesspalace.tusky.entity.Status -import android.view.View; - -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public interface StatusActionListener extends LinkListener { - void onReply(int position); - void onReblog(final boolean reblog, final int position); - void onFavourite(final boolean favourite, final int position); - void onBookmark(final boolean bookmark, final int position); - void onMore(@NonNull View view, final int position); - void onViewMedia(int position, int attachmentIndex, @Nullable View view); - void onViewThread(int position); +interface StatusActionListener : LinkListener { + fun onReply(position: Int) + fun onReblog(reblog: Boolean, position: Int, visibility: Status.Visibility = Status.Visibility.PUBLIC) + fun onFavourite(favourite: Boolean, position: Int) + fun onBookmark(bookmark: Boolean, position: Int) + fun onMore(view: View, position: Int) + fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) + fun onViewThread(position: Int) /** * Open reblog author for the status. * @param position At which position in the list status is located */ - void onOpenReblog(int position); - void onExpandedChange(boolean expanded, int position); - void onContentHiddenChange(boolean isShowing, int position); - void onLoadMore(int position); + fun onOpenReblog(position: Int) + fun onExpandedChange(expanded: Boolean, position: Int) + fun onContentHiddenChange(isShowing: Boolean, position: Int) + fun onLoadMore(position: Int) /** - * Called when the status {@link android.widget.ToggleButton} responsible for collapsing long + * Called when the status [android.widget.ToggleButton] responsible for collapsing long * status content is interacted with. * * @param isCollapsed Whether the status content is shown in a collapsed state or fully. * @param position The position of the status in the list. */ - void onContentCollapsedChange(boolean isCollapsed, int position); + fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) /** * called when the reblog count has been clicked * @param position The position of the status in the list. */ - default void onShowReblogs(int position) {} + fun onShowReblogs(position: Int) {} /** * called when the favourite count has been clicked * @param position The position of the status in the list. */ - default void onShowFavs(int position) {} + fun onShowFavs(position: Int) {} - void onVoteInPoll(int position, @NonNull List choices); + fun onVoteInPoll(position: Int, choices: List) - default void onShowEdits(int position) {} + fun onShowEdits(position: Int) {} - void clearWarningAction(int position); + fun clearWarningAction(position: Int) - void onUntranslate(int position); + fun onUntranslate(position: Int) } diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 3042a8fae..40617328b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -252,8 +252,9 @@ interface MastodonApi { @DELETE("api/v1/statuses/{id}") suspend fun deleteStatus(@Path("id") statusId: String): NetworkResult + @FormUrlEncoded @POST("api/v1/statuses/{id}/reblog") - suspend fun reblogStatus(@Path("id") statusId: String): NetworkResult + suspend fun reblogStatus(@Path("id") statusId: String, @Field("visibility") visibility: String?): NetworkResult @POST("api/v1/statuses/{id}/unreblog") suspend fun unreblogStatus(@Path("id") statusId: String): NetworkResult diff --git a/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt b/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt index 805b73669..2e1cfddcd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt +++ b/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt @@ -44,9 +44,9 @@ class TimelineCases @Inject constructor( private val eventHub: EventHub ) { - suspend fun reblog(statusId: String, reblog: Boolean): NetworkResult { + suspend fun reblog(statusId: String, reblog: Boolean, visibility: Status.Visibility = Status.Visibility.PUBLIC): NetworkResult { return if (reblog) { - mastodonApi.reblogStatus(statusId) + mastodonApi.reblogStatus(statusId, visibility.stringValue) } else { mastodonApi.unreblogStatus(statusId) }.onSuccess { status -> diff --git a/app/src/main/res/menu/status_reblog.xml b/app/src/main/res/menu/status_reblog.xml index 4d79e6b30..ee4440cf1 100644 --- a/app/src/main/res/menu/status_reblog.xml +++ b/app/src/main/res/menu/status_reblog.xml @@ -1,9 +1,23 @@ - + + + + + + + + Quick Reply Reply Boost + Followers-Only Boost + Public Boost + Unlisted Boost Remove boost Favorite Remove favorite