Compare commits
5 Commits
master
...
feature/di
Author | SHA1 | Date |
---|---|---|
LucasGGamerM | 104423b5e8 | |
LucasGGamerM | 521c742d1a | |
LucasGGamerM | 135a98224f | |
LucasGGamerM | bed71fd9e0 | |
LucasGGamerM | c695bb6aa3 |
|
@ -0,0 +1,22 @@
|
||||||
|
package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetConversationsTimeline extends MastodonAPIRequest<List<Status>>{
|
||||||
|
public GetConversationsTimeline(String maxID, String minID, int limit, String sinceID){
|
||||||
|
super(HttpMethod.GET, "/conversations", new TypeToken<>(){});
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(minID!=null)
|
||||||
|
addQueryParameter("min_id", minID);
|
||||||
|
if(sinceID!=null)
|
||||||
|
addQueryParameter("since_id", sinceID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", ""+limit);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetConversationsTimeline;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class ConversationsTimelineFragment extends FabStatusListFragment {
|
||||||
|
private HomeTabFragment parent;
|
||||||
|
private String maxID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
if (getParentFragment() instanceof HomeTabFragment home) parent = home;
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Status> filterPosts(List<Status> items) {
|
||||||
|
// Disabling this for DMs, because there are no boosts on DMs, and most of them are replies
|
||||||
|
return items.stream().filter(i ->
|
||||||
|
(i.visibility == StatusPrivacy.DIRECT)
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
// return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
AccountSessionManager.getInstance()
|
||||||
|
.getAccount(accountID).getCacheController()
|
||||||
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
|
maxID=result.maxID;
|
||||||
|
if(result.isFromCache())
|
||||||
|
loadNewPosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
|
if(parent != null && parent.isNewPostsBtnShown() && list.getChildAdapterPosition(list.getChildAt(0))<=getMainAdapterOffset()){
|
||||||
|
parent.hideNewPostsButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad")){
|
||||||
|
if(!loaded && !dataLoading){
|
||||||
|
loadData();
|
||||||
|
}else if(!dataLoading){
|
||||||
|
loadNewPosts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStatusCreated(StatusCreatedEvent ev){
|
||||||
|
prependItems(Collections.singletonList(ev.status), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadNewPosts(){
|
||||||
|
if (!GlobalUserPreferences.loadNewPosts) return;
|
||||||
|
dataLoading=true;
|
||||||
|
// The idea here is that we request the timeline such that if there are fewer than `limit` posts,
|
||||||
|
// we'll get the currently topmost post as last in the response. This way we know there's no gap
|
||||||
|
// between the existing and newly loaded parts of the timeline.
|
||||||
|
String sinceID=data.size()>1 ? data.get(1).id : "1";
|
||||||
|
currentRequest=new GetConversationsTimeline(null, null, 20, sinceID)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
result = filterPosts(result);
|
||||||
|
if(result.isEmpty() || getActivity()==null)
|
||||||
|
return;
|
||||||
|
Status last=result.get(result.size()-1);
|
||||||
|
List<Status> toAdd;
|
||||||
|
if(!data.isEmpty() && last.id.equals(data.get(0).id)){ // This part intersects with the existing one
|
||||||
|
toAdd=result.subList(0, result.size()-1); // Remove the already known last post
|
||||||
|
}else{
|
||||||
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
|
toAdd=result;
|
||||||
|
}
|
||||||
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||||
|
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||||
|
if(!toAdd.isEmpty()){
|
||||||
|
prependItems(toAdd, true);
|
||||||
|
if (parent != null) parent.showNewPostsButton();
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomTimeline(toAdd, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGapClick(GapStatusDisplayItem.Holder item){
|
||||||
|
if(dataLoading)
|
||||||
|
return;
|
||||||
|
item.getItem().loading=true;
|
||||||
|
V.setVisibilityAnimated(item.progress, View.VISIBLE);
|
||||||
|
V.setVisibilityAnimated(item.text, View.GONE);
|
||||||
|
GapStatusDisplayItem gap=item.getItem();
|
||||||
|
dataLoading=true;
|
||||||
|
currentRequest=new GetConversationsTimeline(item.getItemID(), null, 20, null)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
int gapPos=displayItems.indexOf(gap);
|
||||||
|
if(gapPos==-1)
|
||||||
|
return;
|
||||||
|
if(result.isEmpty()){
|
||||||
|
displayItems.remove(gapPos);
|
||||||
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
|
Status gapStatus=getStatusByID(gap.parentID);
|
||||||
|
if(gapStatus!=null){
|
||||||
|
gapStatus.hasGapAfter=false;
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(Collections.singletonList(gapStatus), false);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Set<String> idsBelowGap=new HashSet<>();
|
||||||
|
boolean belowGap=false;
|
||||||
|
int gapPostIndex=0;
|
||||||
|
for(Status s:data){
|
||||||
|
if(belowGap){
|
||||||
|
idsBelowGap.add(s.id);
|
||||||
|
}else if(s.id.equals(gap.parentID)){
|
||||||
|
belowGap=true;
|
||||||
|
s.hasGapAfter=false;
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(Collections.singletonList(s), false);
|
||||||
|
}else{
|
||||||
|
gapPostIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int endIndex=0;
|
||||||
|
for(Status s:result){
|
||||||
|
endIndex++;
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(endIndex==result.size()){
|
||||||
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
|
}else{
|
||||||
|
result=result.subList(0, endIndex);
|
||||||
|
}
|
||||||
|
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
||||||
|
targetList.clear();
|
||||||
|
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
||||||
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||||
|
for(Status s:result){
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
if(filterPredicate.test(s)){
|
||||||
|
targetList.addAll(buildDisplayItems(s));
|
||||||
|
insertedPosts.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(targetList.isEmpty()){
|
||||||
|
// oops. We didn't add new posts, but at least we know there are none.
|
||||||
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
|
||||||
|
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
|
||||||
|
}
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(insertedPosts, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
gap.loading=false;
|
||||||
|
Activity a=getActivity();
|
||||||
|
if(a!=null){
|
||||||
|
error.showToast(a);
|
||||||
|
int gapPos=displayItems.indexOf(gap);
|
||||||
|
if(gapPos>=0)
|
||||||
|
adapter.notifyItemChanged(gapPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRefresh(){
|
||||||
|
if(currentRequest!=null){
|
||||||
|
currentRequest.cancel();
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
}
|
||||||
|
if (parent != null) parent.hideNewPostsButton();
|
||||||
|
super.onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
private TabLayoutMediator tabLayoutMediator;
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
|
|
||||||
private NotificationsListFragment allNotificationsFragment, mentionsFragment, postsFragment;
|
private NotificationsListFragment allNotificationsFragment, mentionsFragment, conversationsFragment;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
|
||||||
|
@ -151,14 +151,14 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
mentionsFragment.setArguments(args);
|
mentionsFragment.setArguments(args);
|
||||||
|
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
args.putBoolean("onlyPosts", true);
|
args.putBoolean("onlyConversations", true);
|
||||||
postsFragment=new NotificationsListFragment();
|
conversationsFragment =new NotificationsListFragment();
|
||||||
postsFragment.setArguments(args);
|
conversationsFragment.setArguments(args);
|
||||||
|
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.add(R.id.notifications_all, allNotificationsFragment)
|
.add(R.id.notifications_all, allNotificationsFragment)
|
||||||
.add(R.id.notifications_mentions, mentionsFragment)
|
.add(R.id.notifications_mentions, mentionsFragment)
|
||||||
.add(R.id.notifications_posts, postsFragment)
|
.add(R.id.notifications_posts, conversationsFragment)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
tab.setText(switch(position){
|
tab.setText(switch(position){
|
||||||
case 0 -> R.string.all_notifications;
|
case 0 -> R.string.all_notifications;
|
||||||
case 1 -> R.string.mentions;
|
case 1 -> R.string.mentions;
|
||||||
case 2 -> R.string.posts;
|
case 2 -> R.string.sk_conversations;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||||
});
|
});
|
||||||
tab.view.textView.setAllCaps(true);
|
tab.view.textView.setAllCaps(true);
|
||||||
|
@ -217,7 +217,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
return switch(page){
|
return switch(page){
|
||||||
case 0 -> allNotificationsFragment;
|
case 0 -> allNotificationsFragment;
|
||||||
case 1 -> mentionsFragment;
|
case 1 -> mentionsFragment;
|
||||||
case 2 -> postsFragment;
|
case 2 -> conversationsFragment;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,21 @@ import com.squareup.otto.Subscribe;
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetConversationsTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -34,12 +38,16 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||||
private boolean onlyMentions;
|
private boolean onlyMentions;
|
||||||
private boolean onlyPosts;
|
private boolean onlyPosts;
|
||||||
|
|
||||||
|
private boolean onlyConversations;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,6 +67,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
onlyMentions=getArguments().getBoolean("onlyMentions", false);
|
onlyMentions=getArguments().getBoolean("onlyMentions", false);
|
||||||
onlyPosts=getArguments().getBoolean("onlyPosts", false);
|
onlyPosts=getArguments().getBoolean("onlyPosts", false);
|
||||||
|
onlyConversations=getArguments().getBoolean("onlyConversations", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -112,26 +121,27 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
AccountSessionManager.getInstance()
|
AccountSessionManager.getInstance()
|
||||||
.getAccount(accountID).getCacheController()
|
.getAccount(accountID).getCacheController()
|
||||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
.getNotifications(offset > 0 ? maxID : null, count, onlyMentions, onlyPosts || onlyConversations, refreshing, new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
public void onSuccess(PaginatedResponse<List<Notification>> result) {
|
||||||
if(getActivity()==null)
|
if (getActivity() == null)
|
||||||
return;
|
return;
|
||||||
if(refreshing)
|
if (refreshing)
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
onDataLoaded(result.items.stream().filter(n -> n.type != null || (onlyConversations &&)).collect(Collectors.toList()), !result.items.isEmpty());
|
||||||
Set<String> needRelationships=result.items.stream()
|
Set<String> needRelationships = result.items.stream()
|
||||||
.filter(ntf->ntf.status==null && !relationships.containsKey(ntf.account.id))
|
.filter(ntf -> ntf.status == null && !relationships.containsKey(ntf.account.id))
|
||||||
.map(ntf->ntf.account.id)
|
.map(ntf -> ntf.account.id)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
loadRelationships(needRelationships);
|
loadRelationships(needRelationships);
|
||||||
maxID=result.maxID;
|
maxID = result.maxID;
|
||||||
|
|
||||||
if(offset==0 && !result.items.isEmpty()){
|
if (offset == 0 && !result.items.isEmpty()) {
|
||||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -163,6 +163,7 @@
|
||||||
<string name="sk_remove_follower_confirm">Remove %s as a follower by blocking and immediately unblocking them?</string>
|
<string name="sk_remove_follower_confirm">Remove %s as a follower by blocking and immediately unblocking them?</string>
|
||||||
<string name="sk_do_remove_follower">Remove</string>
|
<string name="sk_do_remove_follower">Remove</string>
|
||||||
<string name="sk_remove_follower_success">Successfully removed follower</string>
|
<string name="sk_remove_follower_success">Successfully removed follower</string>
|
||||||
|
<string name="sk_conversations">Conversations</string>
|
||||||
|
|
||||||
<!-- accessibility labels-->
|
<!-- accessibility labels-->
|
||||||
<string name="sk_poll_option_add">Add new poll option</string>
|
<string name="sk_poll_option_add">Add new poll option</string>
|
||||||
|
|
Loading…
Reference in New Issue