Add severed_relationships
notifications (AND-174)
This commit is contained in:
parent
9533b4f45d
commit
16e2632d9b
@ -11,6 +11,7 @@ import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.viewmodel.NotificationViewModel;
|
||||
import org.joinmastodon.android.ui.displayitems.InlineStatusStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.NotificationWithButtonStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.parceler.Parcels;
|
||||
@ -35,7 +36,10 @@ public abstract class BaseNotificationsListFragment extends BaseStatusListFragme
|
||||
else
|
||||
titleItem=null;
|
||||
}else{
|
||||
titleItem=new NotificationHeaderStatusDisplayItem(n.getID(), this, n, accountID);
|
||||
if(n.notification.type==NotificationType.SEVERED_RELATIONSHIPS)
|
||||
titleItem=new NotificationWithButtonStatusDisplayItem(n.getID(), this, n, accountID);
|
||||
else
|
||||
titleItem=new NotificationHeaderStatusDisplayItem(n.getID(), this, n, accountID);
|
||||
}
|
||||
if(n.status!=null){
|
||||
if(titleItem!=null && n.notification.type!=NotificationType.STATUS){
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
@ -14,10 +16,9 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||
public NotificationType type;
|
||||
@RequiredField
|
||||
public Instant createdAt;
|
||||
@RequiredField
|
||||
public Account account;
|
||||
|
||||
public Status status;
|
||||
public RelationshipSeveranceEvent event;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@ -25,6 +26,17 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||
account.postprocess();
|
||||
if(status!=null)
|
||||
status.postprocess();
|
||||
if(event!=null){
|
||||
try{
|
||||
event.postprocess();
|
||||
}catch(ObjectValidationException x){
|
||||
Log.w("Notification", x);
|
||||
event=null;
|
||||
}
|
||||
}
|
||||
if(type!=NotificationType.SEVERED_RELATIONSHIPS && account==null){
|
||||
throw new ObjectValidationException("account must be present for type "+type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,8 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
|
||||
import java.time.Instant;
|
||||
@ -18,7 +21,23 @@ public class NotificationGroup extends BaseModel{
|
||||
@RequiredField
|
||||
public List<String> sampleAccountIds;
|
||||
public String statusId;
|
||||
// TODO report
|
||||
// TODO event
|
||||
public RelationshipSeveranceEvent event;
|
||||
// TODO moderation_warning
|
||||
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
if(event!=null){
|
||||
try{
|
||||
event.postprocess();
|
||||
}catch(ObjectValidationException x){
|
||||
Log.w("Notification", x);
|
||||
event=null;
|
||||
}
|
||||
}
|
||||
if(type!=NotificationType.SEVERED_RELATIONSHIPS && sampleAccountIds.isEmpty()){
|
||||
throw new ObjectValidationException("sample_account_ids must be present for type "+type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ public enum NotificationType{
|
||||
@SerializedName("status")
|
||||
STATUS,
|
||||
@SerializedName("update")
|
||||
UPDATE;
|
||||
UPDATE,
|
||||
@SerializedName("severed_relationships")
|
||||
SEVERED_RELATIONSHIPS;
|
||||
|
||||
public boolean canBeGrouped(){
|
||||
return this==REBLOG || this==FAVORITE;
|
||||
|
@ -0,0 +1,30 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Parcel
|
||||
public class RelationshipSeveranceEvent extends BaseModel{
|
||||
public String id;
|
||||
@RequiredField
|
||||
public Type type;
|
||||
public boolean purged;
|
||||
@RequiredField
|
||||
public String targetName;
|
||||
public int followersCount;
|
||||
public int followingCount;
|
||||
public Instant createdAt;
|
||||
|
||||
public enum Type{
|
||||
@SerializedName("domain_block")
|
||||
DOMAIN_BLOCK,
|
||||
@SerializedName("user_domain_block")
|
||||
USER_DOMAIN_BLOCK,
|
||||
@SerializedName("account_suspension")
|
||||
ACCOUNT_SUSPENSION
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.NotificationType;
|
||||
import org.joinmastodon.android.model.RelationshipSeveranceEvent;
|
||||
import org.joinmastodon.android.model.viewmodel.NotificationViewModel;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class NotificationWithButtonStatusDisplayItem extends StatusDisplayItem{
|
||||
private final NotificationViewModel notification;
|
||||
private CharSequence text;
|
||||
private String buttonText;
|
||||
private Runnable buttonAction;
|
||||
|
||||
public NotificationWithButtonStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, NotificationViewModel notification, String accountID){
|
||||
super(parentID, parentFragment);
|
||||
this.notification=notification;
|
||||
if(notification.notification.type==NotificationType.SEVERED_RELATIONSHIPS){
|
||||
RelationshipSeveranceEvent event=notification.notification.event;
|
||||
String localDomain=AccountSessionManager.get(accountID).domain;
|
||||
if(event!=null){
|
||||
text=switch(event.type){
|
||||
case ACCOUNT_SUSPENSION -> replacePlaceholdersWithBoldStrings(parentFragment.getString(R.string.relationship_severance_account_suspension,
|
||||
"{{localDomain}}", "{{target}}"), Map.of("localDomain", localDomain, "target", event.targetName));
|
||||
case DOMAIN_BLOCK -> replacePlaceholdersWithBoldStrings(parentFragment.getString(R.string.relationship_severance_domain_block,
|
||||
"{{localDomain}}", "{{target}}", event.followersCount, parentFragment.getResources().getQuantityString(R.plurals.x_accounts, event.followingCount, event.followingCount)),
|
||||
Map.of("localDomain", localDomain, "target", event.targetName));
|
||||
case USER_DOMAIN_BLOCK -> replacePlaceholdersWithBoldStrings(parentFragment.getString(R.string.relationship_severance_user_domain_block,
|
||||
"{{target}}", event.followersCount, parentFragment.getResources().getQuantityString(R.plurals.x_accounts, event.followingCount, event.followingCount)),
|
||||
Map.of("target", event.targetName));
|
||||
};
|
||||
}else{
|
||||
text="???";
|
||||
}
|
||||
buttonText=parentFragment.getString(R.string.relationship_severance_learn_more);
|
||||
buttonAction=()->UiUtils.launchWebBrowser(parentFragment.getActivity(), "https://"+localDomain+"/severed_relationships");
|
||||
}
|
||||
}
|
||||
|
||||
private SpannableStringBuilder replacePlaceholdersWithBoldStrings(String in, Map<String, String> replacements){
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder(in);
|
||||
for(Map.Entry<String, String> e:replacements.entrySet()){
|
||||
String placeholder="{{"+e.getKey()+"}}";
|
||||
int index=ssb.toString().indexOf(placeholder);
|
||||
if(index==-1)
|
||||
continue;
|
||||
ssb.replace(index, index+placeholder.length(), e.getValue());
|
||||
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), index, index+e.getValue().length(), 0);
|
||||
}
|
||||
return ssb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(){
|
||||
return Type.NOTIFICATION_WITH_BUTTON;
|
||||
}
|
||||
|
||||
public static class Holder extends StatusDisplayItem.Holder<NotificationWithButtonStatusDisplayItem>{
|
||||
private final ImageView icon;
|
||||
private final TextView text;
|
||||
private final Button button;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_notification_with_button, parent);
|
||||
icon=findViewById(R.id.icon);
|
||||
text=findViewById(R.id.text);
|
||||
button=findViewById(R.id.button);
|
||||
button.setOnClickListener(v->item.buttonAction.run());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(NotificationWithButtonStatusDisplayItem item){
|
||||
icon.setImageResource(switch(item.notification.notification.type){
|
||||
case SEVERED_RELATIONSHIPS -> R.drawable.ic_heart_broken_fill1_24px;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + item.notification.notification.type);
|
||||
});
|
||||
icon.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3Outline)));
|
||||
text.setText(item.text);
|
||||
button.setText(item.buttonText);
|
||||
button.setEnabled(item.buttonAction!=null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -81,6 +81,7 @@ public abstract class StatusDisplayItem{
|
||||
case SECTION_HEADER -> new SectionHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case INLINE_STATUS -> new InlineStatusStatusDisplayItem.Holder(activity, parent);
|
||||
case NOTIFICATION_WITH_BUTTON -> new NotificationWithButtonStatusDisplayItem.Holder(activity, parent);
|
||||
};
|
||||
}
|
||||
|
||||
@ -226,7 +227,8 @@ public abstract class StatusDisplayItem{
|
||||
HEADER_CHECKABLE,
|
||||
NOTIFICATION_HEADER,
|
||||
FILTER_SPOILER,
|
||||
INLINE_STATUS
|
||||
INLINE_STATUS,
|
||||
NOTIFICATION_WITH_BUTTON
|
||||
}
|
||||
|
||||
public static abstract class Holder<T> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
||||
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M481,877Q347,742 267.5,659Q188,576 146.5,521Q105,466 92.5,427Q80,388 80,340Q80,248 144,184Q208,120 300,120Q345,120 387,136.5Q429,153 462,184L400,400L520,400L486,735L600,360L480,360L551,148Q576,134 603.5,127Q631,120 660,120Q752,120 816,184Q880,248 880,340Q880,388 867,428Q854,468 812,523.5Q770,579 691,661.5Q612,744 481,877Z"/>
|
||||
</vector>
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center"
|
||||
tools:tint="#0f0"
|
||||
tools:src="@drawable/ic_repeat_24px"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:minHeight="20dp"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="Notification text"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:layout_below="@id/text"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_marginStart="-8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/bg_button_m3_text"
|
||||
android:textColor="?colorM3Primary"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:minWidth="0dp"
|
||||
android:gravity="start|center_vertical"
|
||||
tools:text="Button text"/>
|
||||
|
||||
</RelativeLayout>
|
@ -798,4 +798,14 @@
|
||||
<string name="own_poll_ended">Your poll has ended</string>
|
||||
<string name="user_just_posted">%s just posted</string>
|
||||
<string name="user_edited_post">%s edited a post you interacted with</string>
|
||||
<string name="relationship_severance_account_suspension">An admin from %1$s has suspended %2$s, which means you can no longer receive updates from them or interact with them.</string>
|
||||
<!-- %1$s is your server domain, %2$s is the domain that was blocked, %3$,d is the follower count, %4$s is the `x_accounts` plural string -->
|
||||
<string name="relationship_severance_domain_block">An admin from %1$s has blocked %2$s, including %3$,d of your followers and %4$s you follow.</string>
|
||||
<plurals name="x_accounts">
|
||||
<item quantity="one">%,d account</item>
|
||||
<item quantity="other">%,d accounts</item>
|
||||
</plurals>
|
||||
<!-- %1$s is the domain that was blocked, %2$,d is the follower count, %3$s is the `x_accounts` plural string -->
|
||||
<string name="relationship_severance_user_domain_block">You have blocked %1$s, removing %2$,d of your followers and %3$s you follow.</string>
|
||||
<string name="relationship_severance_learn_more">Learn more</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user