Filtered posts in timelines (AND-8)
This commit is contained in:
@@ -16,19 +16,16 @@ import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.FilterContext;
|
||||
import org.joinmastodon.android.model.LegacyFilter;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.model.SearchResult;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
@@ -60,7 +57,6 @@ public class CacheController{
|
||||
cancelDelayedClose();
|
||||
databaseThread.postRunnable(()->{
|
||||
try{
|
||||
List<LegacyFilter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(FilterContext.HOME)).collect(Collectors.toList());
|
||||
if(!forceReload){
|
||||
SQLiteDatabase db=getOrOpenDatabase();
|
||||
try(Cursor cursor=db.query("home_timeline", new String[]{"json", "flags"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`time` DESC", count+"")){
|
||||
@@ -68,20 +64,16 @@ public class CacheController{
|
||||
ArrayList<Status> result=new ArrayList<>();
|
||||
cursor.moveToFirst();
|
||||
String newMaxID;
|
||||
outer:
|
||||
do{
|
||||
Status status=MastodonAPIController.gson.fromJson(cursor.getString(0), Status.class);
|
||||
status.postprocess();
|
||||
int flags=cursor.getInt(1);
|
||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||
newMaxID=status.id;
|
||||
for(LegacyFilter filter:filters){
|
||||
if(filter.matches(status))
|
||||
continue outer;
|
||||
}
|
||||
result.add(status);
|
||||
}while(cursor.moveToNext());
|
||||
String _newMaxID=newMaxID;
|
||||
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.HOME);
|
||||
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
||||
return;
|
||||
}
|
||||
@@ -93,7 +85,9 @@ public class CacheController{
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
ArrayList<Status> filtered=new ArrayList<>(result);
|
||||
AccountSessionManager.get(accountID).filterStatuses(filtered, FilterContext.HOME);
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(filtered, result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
putHomeTimeline(result, maxID==null);
|
||||
}
|
||||
|
||||
@@ -140,7 +134,6 @@ public class CacheController{
|
||||
}
|
||||
return;
|
||||
}
|
||||
List<LegacyFilter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
||||
if(!forceReload){
|
||||
SQLiteDatabase db=getOrOpenDatabase();
|
||||
try(Cursor cursor=db.query(onlyMentions ? "notifications_mentions" : "notifications_all", new String[]{"json"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`time` DESC", count+"")){
|
||||
@@ -148,20 +141,14 @@ public class CacheController{
|
||||
ArrayList<Notification> result=new ArrayList<>();
|
||||
cursor.moveToFirst();
|
||||
String newMaxID;
|
||||
outer:
|
||||
do{
|
||||
Notification ntf=MastodonAPIController.gson.fromJson(cursor.getString(0), Notification.class);
|
||||
ntf.postprocess();
|
||||
newMaxID=ntf.id;
|
||||
if(ntf.status!=null){
|
||||
for(LegacyFilter filter:filters){
|
||||
if(filter.matches(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
result.add(ntf);
|
||||
}while(cursor.moveToNext());
|
||||
String _newMaxID=newMaxID;
|
||||
AccountSessionManager.get(accountID).filterStatusContainingObjects(result, n->n.status, FilterContext.NOTIFICATIONS);
|
||||
uiHandler.post(()->callback.onSuccess(new PaginatedResponse<>(result, _newMaxID)));
|
||||
return;
|
||||
}
|
||||
@@ -175,16 +162,9 @@ public class CacheController{
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Notification> result){
|
||||
PaginatedResponse<List<Notification>> res=new PaginatedResponse<>(result.stream().filter(ntf->{
|
||||
if(ntf.status!=null){
|
||||
for(LegacyFilter filter:filters){
|
||||
if(filter.matches(ntf.status)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id);
|
||||
ArrayList<Notification> filtered=new ArrayList<>(result);
|
||||
AccountSessionManager.get(accountID).filterStatusContainingObjects(filtered, n->n.status, FilterContext.NOTIFICATIONS);
|
||||
PaginatedResponse<List<Notification>> res=new PaginatedResponse<>(filtered, result.isEmpty() ? null : result.get(result.size()-1).id);
|
||||
callback.onSuccess(res);
|
||||
putNotifications(result, onlyMentions, maxID==null);
|
||||
if(!onlyMentions){
|
||||
|
||||
@@ -9,6 +9,7 @@ public class AccountLocalPreferences{
|
||||
public boolean customEmojiInNames;
|
||||
public boolean showCWs;
|
||||
public boolean hideSensitiveMedia;
|
||||
public boolean serverSideFiltersSupported;
|
||||
|
||||
public AccountLocalPreferences(SharedPreferences prefs){
|
||||
this.prefs=prefs;
|
||||
@@ -16,6 +17,7 @@ public class AccountLocalPreferences{
|
||||
customEmojiInNames=prefs.getBoolean("emojiInNames", true);
|
||||
showCWs=prefs.getBoolean("showCWs", true);
|
||||
hideSensitiveMedia=prefs.getBoolean("hideSensitive", true);
|
||||
serverSideFiltersSupported=prefs.getBoolean("serverSideFilters", false);
|
||||
}
|
||||
|
||||
public long getNotificationsPauseEndTime(){
|
||||
@@ -32,6 +34,7 @@ public class AccountLocalPreferences{
|
||||
.putBoolean("emojiInNames", customEmojiInNames)
|
||||
.putBoolean("showCWs", showCWs)
|
||||
.putBoolean("hideSensitive", hideSensitiveMedia)
|
||||
.putBoolean("serverSideFilters", serverSideFiltersSupported)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,13 @@ import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||
import org.joinmastodon.android.events.NotificationsMarkerUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Application;
|
||||
import org.joinmastodon.android.model.FilterAction;
|
||||
import org.joinmastodon.android.model.FilterContext;
|
||||
import org.joinmastodon.android.model.FilterResult;
|
||||
import org.joinmastodon.android.model.LegacyFilter;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.PushSubscription;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.TimelineMarkers;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
import org.joinmastodon.android.utils.ObjectIdComparator;
|
||||
@@ -31,6 +35,7 @@ import org.joinmastodon.android.utils.ObjectIdComparator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
@@ -213,4 +218,47 @@ public class AccountSession{
|
||||
localPreferences=new AccountLocalPreferences(getRawLocalPreferences());
|
||||
return localPreferences;
|
||||
}
|
||||
|
||||
public void filterStatuses(List<Status> statuses, FilterContext context){
|
||||
filterStatusContainingObjects(statuses, Function.identity(), context);
|
||||
}
|
||||
|
||||
public <T> void filterStatusContainingObjects(List<T> objects, Function<T, Status> extractor, FilterContext context){
|
||||
if(getLocalPreferences().serverSideFiltersSupported){
|
||||
// Even with server-side filters, clients are expected to remove statuses that match a filter that hides them
|
||||
objects.removeIf(o->{
|
||||
Status s=extractor.apply(o);
|
||||
if(s==null)
|
||||
return false;
|
||||
if(s.filtered==null)
|
||||
return false;
|
||||
for(FilterResult filter:s.filtered){
|
||||
if(filter.filter.isActive() && filter.filter.filterAction==FilterAction.HIDE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(wordFilters==null)
|
||||
return;
|
||||
for(T obj:objects){
|
||||
Status s=extractor.apply(obj);
|
||||
if(s!=null && s.filtered!=null){
|
||||
getLocalPreferences().serverSideFiltersSupported=true;
|
||||
getLocalPreferences().save();
|
||||
return;
|
||||
}
|
||||
}
|
||||
objects.removeIf(o->{
|
||||
Status s=extractor.apply(o);
|
||||
if(s==null)
|
||||
return false;
|
||||
for(LegacyFilter filter:wordFilters){
|
||||
if(filter.context.contains(context) && filter.matches(s) && filter.isActive())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ public class AccountSessionManager{
|
||||
if(now-session.infoLastUpdated>24L*3600_000L){
|
||||
updateSessionLocalInfo(session);
|
||||
}
|
||||
if(now-session.filtersLastUpdated>3600_000L){
|
||||
if(!session.getLocalPreferences().serverSideFiltersSupported && now-session.filtersLastUpdated>3600_000L){
|
||||
updateSessionWordFilters(session);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user