Implement muting/unmuting conversations, fix possible appearing of muted users in notifications
This commit is contained in:
parent
ff361c1743
commit
8cb743efaa
@ -259,7 +259,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
switch (concrete.getType()) {
|
||||
case MENTION:
|
||||
case POLL: {
|
||||
if(concrete.getStatusViewData() != null && concrete.getStatusViewData().isMuted())
|
||||
if(concrete.getStatusViewData() != null && concrete.getStatusViewData().isThreadMuted())
|
||||
return VIEW_TYPE_MUTED_STATUS;
|
||||
return VIEW_TYPE_STATUS;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class CacheUpdater @Inject constructor(
|
||||
is ReblogEvent ->
|
||||
timelineDao.setReblogged(accountId, event.statusId, event.reblog)
|
||||
is BookmarkEvent ->
|
||||
timelineDao.setBookmarked(accountId, event.statusId, event.bookmark )
|
||||
timelineDao.setBookmarked(accountId, event.statusId, event.bookmark)
|
||||
is UnfollowEvent ->
|
||||
timelineDao.removeAllByUser(accountId, event.accountId)
|
||||
is StatusDeletedEvent ->
|
||||
@ -52,4 +52,4 @@ class CacheUpdater @Inject constructor(
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.keylesspalace.tusky.entity.Status
|
||||
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
||||
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
||||
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
||||
data class MuteStatusEvent(val statusId: String, val mute: Boolean) : Dispatchable
|
||||
data class UnfollowEvent(val accountId: String) : Dispatchable
|
||||
data class BlockEvent(val accountId: String) : Dispatchable
|
||||
data class MuteEvent(val accountId: String) : Dispatchable
|
||||
|
@ -104,4 +104,4 @@ class NetworkModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create(MastodonApi::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ data class Status(
|
||||
val poll: Poll?,
|
||||
val card: Card?,
|
||||
var content_type: String? = null,
|
||||
val pleroma: PleromaStatus? = null
|
||||
val pleroma: PleromaStatus? = null,
|
||||
var muted: Boolean = false /* set when either thread or user is muted */
|
||||
) {
|
||||
|
||||
val actionableId: String
|
||||
@ -125,9 +126,18 @@ data class Status(
|
||||
)
|
||||
}
|
||||
|
||||
fun isMuted(): Boolean {
|
||||
fun isUserMuted(): Boolean {
|
||||
return muted && !isThreadMuted()
|
||||
}
|
||||
|
||||
fun isThreadMuted(): Boolean {
|
||||
return pleroma?.threadMuted ?: false
|
||||
}
|
||||
|
||||
fun setThreadMuted(mute: Boolean) {
|
||||
if(pleroma?.threadMuted != null)
|
||||
pleroma.threadMuted = mute
|
||||
}
|
||||
|
||||
private fun getEditableText(): String {
|
||||
val builder = SpannableStringBuilder(content)
|
||||
@ -158,7 +168,7 @@ data class Status(
|
||||
}
|
||||
|
||||
data class PleromaStatus(
|
||||
@SerializedName("thread_muted") val threadMuted: Boolean?
|
||||
@SerializedName("thread_muted") var threadMuted: Boolean?
|
||||
)
|
||||
|
||||
data class Mention (
|
||||
|
@ -54,12 +54,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||
import com.keylesspalace.tusky.appstore.EventHub;
|
||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||
import com.keylesspalace.tusky.appstore.*;
|
||||
import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
@ -329,6 +324,15 @@ public class NotificationsFragment extends SFragment implements
|
||||
posAndNotification.second.getStatus(),
|
||||
event.getReblog());
|
||||
}
|
||||
|
||||
private void handleMuteStatusEvent(MuteStatusEvent event) {
|
||||
Pair<Integer, Notification> posAndNotification = findReplyPosition(event.getStatusId());
|
||||
if (posAndNotification == null) return;
|
||||
//noinspection ConstantConditions
|
||||
setMutedStatusForStatus(posAndNotification.first,
|
||||
posAndNotification.second.getStatus(),
|
||||
event.getMute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
@ -381,6 +385,8 @@ public class NotificationsFragment extends SFragment implements
|
||||
handleBookmarkEvent((BookmarkEvent) event);
|
||||
} else if (event instanceof ReblogEvent) {
|
||||
handleReblogEvent((ReblogEvent) event);
|
||||
} else if (event instanceof MuteStatusEvent) {
|
||||
handleMuteStatusEvent((MuteStatusEvent) event);
|
||||
} else if (event instanceof BlockEvent) {
|
||||
removeAllByAccountId(((BlockEvent) event).getAccountId());
|
||||
} else if (event instanceof PreferenceChangedEvent) {
|
||||
@ -441,7 +447,7 @@ public class NotificationsFragment extends SFragment implements
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFavourite(final boolean favourite, final int position) {
|
||||
final Notification notification = notifications.get(position).asRight();
|
||||
@ -600,14 +606,30 @@ public class NotificationsFragment extends SFragment implements
|
||||
(NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||
StatusViewData.Concrete statusViewData =
|
||||
new StatusViewData.Builder(old.getStatusViewData())
|
||||
.setMuted(isMuted)
|
||||
.setThreadMuted(isMuted)
|
||||
.createStatusViewData();
|
||||
Log.d("ASDASDASD", "position = " + position + " isMuted = " + isMuted);
|
||||
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
|
||||
old.getId(), old.getAccount(), statusViewData, old.isExpanded());
|
||||
notifications.setPairedItem(position, notificationViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
private void setMutedStatusForStatus(int position, Status status, boolean muted) {
|
||||
status.setThreadMuted(muted);
|
||||
|
||||
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||
|
||||
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
|
||||
viewDataBuilder.setThreadMuted(muted);
|
||||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.isExpanded());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoadMore(int position) {
|
||||
|
@ -240,6 +240,20 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||
|
||||
replyToItem.setVisible(false);
|
||||
}
|
||||
|
||||
// maybe not a best check
|
||||
if(status.getPleroma() != null) {
|
||||
boolean showMute = true; // predict state
|
||||
|
||||
if(status.isThreadMuted() == true) {
|
||||
showMute = false;
|
||||
}
|
||||
|
||||
// show mutes only for Pleroma because Mastodon don't handle them in sane way
|
||||
// e.g. why you can only mute threads where you were participated?
|
||||
menu.findItem(R.id.status_mute_conversation).setVisible(showMute);
|
||||
menu.findItem(R.id.status_unmute_conversation).setVisible(!showMute);
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
switch (item.getItemId()) {
|
||||
@ -298,6 +312,14 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||
openReportPage(accountId, accountUsername, id);
|
||||
return true;
|
||||
}
|
||||
case R.id.status_mute_conversation: {
|
||||
timelineCases.muteStatus(status, true);
|
||||
return true;
|
||||
}
|
||||
case R.id.status_unmute_conversation: {
|
||||
timelineCases.muteStatus(status, false);
|
||||
return true;
|
||||
}
|
||||
case R.id.status_unreblog_private: {
|
||||
onReblog(false, position);
|
||||
return true;
|
||||
|
@ -206,7 +206,17 @@ interface MastodonApi {
|
||||
fun unpinStatus(
|
||||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
|
||||
@POST("api/v1/statuses/{id}/mute")
|
||||
fun muteStatus(
|
||||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
@POST("api/v1/statuses/{id}/unmute")
|
||||
fun unmuteStatus(
|
||||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
@GET("api/v1/scheduled_statuses")
|
||||
fun scheduledStatuses(
|
||||
@Query("limit") limit: Int? = null,
|
||||
|
@ -27,6 +27,7 @@ import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.lang.IllegalStateException
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* Created by charlag on 3/24/18.
|
||||
@ -36,6 +37,7 @@ interface TimelineCases {
|
||||
fun reblog(status: Status, reblog: Boolean): Single<Status>
|
||||
fun favourite(status: Status, favourite: Boolean): Single<Status>
|
||||
fun bookmark(status: Status, bookmark: Boolean): Single<Status>
|
||||
fun muteStatus(status: Status, mute: Boolean)
|
||||
fun mute(id: String)
|
||||
fun block(id: String)
|
||||
fun delete(id: String): Single<DeletedStatus>
|
||||
@ -103,6 +105,18 @@ class TimelineCasesImpl(
|
||||
})
|
||||
eventHub.dispatch(MuteEvent(id))
|
||||
}
|
||||
|
||||
override fun muteStatus(status: Status, mute: Boolean) {
|
||||
val id = status.actionableId
|
||||
|
||||
(if (mute) {
|
||||
mastodonApi.muteStatus(id)
|
||||
} else {
|
||||
mastodonApi.unmuteStatus(id)
|
||||
}).subscribe( { status ->
|
||||
eventHub.dispatch(MuteStatusEvent(status.id, mute))
|
||||
}, {})
|
||||
}
|
||||
|
||||
override fun block(id: String) {
|
||||
val call = mastodonApi.blockAccount(id)
|
||||
@ -143,4 +157,4 @@ class TimelineCasesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ public class NotificationHelper {
|
||||
}
|
||||
|
||||
// Pleroma extension: don't notify about seen notifications
|
||||
if (body.getPleroma() != null && body.getPleroma().getSeen()) {
|
||||
if (body.getPleroma() != null && body.getPleroma().getSeen() == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (body.getStatus() != null
|
||||
&& body.getStatus().getPleroma() != null
|
||||
&& body.getStatus().getPleroma().getThreadMuted() == true) {
|
||||
if (body.getStatus() != null &&
|
||||
(body.getStatus().isUserMuted() == true ||
|
||||
body.getStatus().isThreadMuted() == true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,8 @@ public final class ViewDataUtils {
|
||||
.setPoll(visibleStatus.getPoll())
|
||||
.setCard(visibleStatus.getCard())
|
||||
.setIsBot(visibleStatus.getAccount().getBot())
|
||||
.setMuted(visibleStatus.isMuted())
|
||||
.setUserMuted(visibleStatus.isUserMuted())
|
||||
.setThreadMuted(visibleStatus.isThreadMuted())
|
||||
.createStatusViewData();
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,8 @@ public abstract class StatusViewData {
|
||||
@Nullable
|
||||
private final PollViewData poll;
|
||||
private final boolean isBot;
|
||||
private final boolean isMuted;
|
||||
private final boolean isThreadMuted;
|
||||
private final boolean isUserMuted;
|
||||
|
||||
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked,
|
||||
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
||||
@ -100,7 +101,8 @@ public abstract class StatusViewData {
|
||||
Date createdAt, int reblogsCount, int favouritesCount, @Nullable String inReplyToId,
|
||||
@Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
|
||||
Status.Application application, List<Emoji> statusEmojis, List<Emoji> accountEmojis, @Nullable Card card,
|
||||
boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isMuted) {
|
||||
boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot, boolean isThreadMuted,
|
||||
boolean isUserMuted) {
|
||||
|
||||
this.id = id;
|
||||
if (Build.VERSION.SDK_INT == 23) {
|
||||
@ -140,7 +142,8 @@ public abstract class StatusViewData {
|
||||
this.isCollapsed = isCollapsed;
|
||||
this.poll = poll;
|
||||
this.isBot = isBot;
|
||||
this.isMuted = isMuted;
|
||||
this.isThreadMuted = isThreadMuted;
|
||||
this.isUserMuted = isUserMuted;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@ -289,8 +292,12 @@ public abstract class StatusViewData {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return isMuted;
|
||||
public boolean isThreadMuted() {
|
||||
return isThreadMuted;
|
||||
}
|
||||
|
||||
public boolean isUserMuted() {
|
||||
return isUserMuted;
|
||||
}
|
||||
|
||||
public boolean deepEquals(StatusViewData o) {
|
||||
@ -327,7 +334,8 @@ public abstract class StatusViewData {
|
||||
Objects.equals(card, concrete.card) &&
|
||||
Objects.equals(poll, concrete.poll) &&
|
||||
isCollapsed == concrete.isCollapsed &&
|
||||
isMuted == concrete.isMuted;
|
||||
isThreadMuted == concrete.isThreadMuted &&
|
||||
isUserMuted == concrete.isUserMuted;
|
||||
}
|
||||
|
||||
static Spanned replaceCrashingCharacters(Spanned content) {
|
||||
@ -434,7 +442,8 @@ public abstract class StatusViewData {
|
||||
private boolean isCollapsed; /** Whether the status is shown partially or fully */
|
||||
private PollViewData poll;
|
||||
private boolean isBot;
|
||||
private boolean isMuted;
|
||||
private boolean isThreadMuted;
|
||||
private boolean isUserMuted;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
@ -471,7 +480,8 @@ public abstract class StatusViewData {
|
||||
isCollapsed = viewData.isCollapsed();
|
||||
poll = viewData.poll;
|
||||
isBot = viewData.isBot();
|
||||
isMuted = viewData.isMuted;
|
||||
isThreadMuted = viewData.isThreadMuted;
|
||||
isUserMuted = viewData.isUserMuted;
|
||||
}
|
||||
|
||||
public Builder setId(String id) {
|
||||
@ -643,8 +653,13 @@ public abstract class StatusViewData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMuted(Boolean isMuted) {
|
||||
this.isMuted = isMuted;
|
||||
public Builder setUserMuted(Boolean isUserMuted) {
|
||||
this.isUserMuted = isUserMuted;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setThreadMuted(Boolean isThreadMuted) {
|
||||
this.isThreadMuted = isThreadMuted;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -657,7 +672,7 @@ public abstract class StatusViewData {
|
||||
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
||||
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
||||
statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot, isMuted);
|
||||
statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot, isThreadMuted, isUserMuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,14 @@
|
||||
<item
|
||||
android:id="@+id/status_open_as"
|
||||
android:title="@string/action_open_as" />
|
||||
<item
|
||||
android:id="@+id/status_mute_conversation"
|
||||
android:title="@string/action_mute_conversation"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/status_unmute_conversation"
|
||||
android:title="@string/action_unmute_conversation"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/status_download_media"
|
||||
android:title="@string/download_media" />
|
||||
|
@ -21,6 +21,14 @@
|
||||
<item
|
||||
android:id="@+id/status_open_as"
|
||||
android:title="@string/action_open_as" />
|
||||
<item
|
||||
android:id="@+id/status_mute_conversation"
|
||||
android:title="@string/action_mute_conversation"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/status_unmute_conversation"
|
||||
android:title="@string/action_unmute_conversation"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/status_reblog_private"
|
||||
android:title="@string/reblog_private"
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
<string name="action_reply_to">Reply to</string>
|
||||
<string name="action_markdown">Markdown</string>
|
||||
<!--<string name="action_mute">-->
|
||||
<string name="action_mute_conversation">Mute conversation</string>
|
||||
<string name="action_unmute_conversation">Unmute conversation</string>
|
||||
|
||||
<string name="hint_appname">Application name</string>
|
||||
<string name="hint_website">Application website</string>
|
||||
|
Loading…
Reference in New Issue
Block a user