More Akkoma improvements (#524)
* Only open account if domain matches Akkoma will seemingly show results that don't match well. This checks if the domain matches before continuing * Add "RE:" for quotes where it's missing * Fix no hashtag history in search * Skip not implemented discovery and select search on Pleroma * Set proper max account fields for Pleroma * Use Pleroma's non-standard poll limits * Mark notifications as read properly on Pleroma * Akkoma bubble timeline * Respect Reply Visibility preference on all timelines * vertically center if hashtag has no history * only open account search result if uri equals * add getInstance and isPleroma methods * change timelines api, support compatibility checks --------- Co-authored-by: sk <sk22@mailbox.org>
This commit is contained in:
parent
231f19d113
commit
b5f6687925
|
@ -19,7 +19,6 @@ import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
|
||||||
import org.joinmastodon.android.model.SearchResult;
|
import org.joinmastodon.android.model.SearchResult;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
@ -160,7 +159,7 @@ public class CacheController{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(accountSession.domain);
|
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(accountSession.domain);
|
||||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.pleroma != null)
|
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.isPleroma())
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Notification> result){
|
public void onSuccess(List<Notification> result){
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.joinmastodon.android.api.requests.notifications;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Notification;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
|
||||||
|
public class PleromaMarkNotificationsRead extends MastodonAPIRequest<List<Notification>> {
|
||||||
|
private String maxID;
|
||||||
|
public PleromaMarkNotificationsRead(String maxID) {
|
||||||
|
super(HttpMethod.POST, "/pleroma/notifications/read", new TypeToken<>(){});
|
||||||
|
this.maxID = maxID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RequestBody getRequestBody() {
|
||||||
|
MultipartBody.Builder builder=new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM);
|
||||||
|
if(!TextUtils.isEmpty(maxID))
|
||||||
|
builder.addFormDataPart("max_id", maxID);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetBubbleTimeline extends MastodonAPIRequest<List<Status>> {
|
||||||
|
public GetBubbleTimeline(String maxID, int limit) {
|
||||||
|
super(HttpMethod.GET, "/timelines/bubble", new TypeToken<>(){});
|
||||||
|
if(!TextUtils.isEmpty(maxID))
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", limit+"");
|
||||||
|
if(GlobalUserPreferences.replyVisibility != null)
|
||||||
|
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
@ -16,5 +17,7 @@ public class GetHashtagTimeline extends MastodonAPIRequest<List<Status>>{
|
||||||
addQueryParameter("min_id", minID);
|
addQueryParameter("min_id", minID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
|
if(GlobalUserPreferences.replyVisibility != null)
|
||||||
|
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
@ -18,5 +19,7 @@ public class GetListTimeline extends MastodonAPIRequest<List<Status>> {
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(sinceID!=null)
|
if(sinceID!=null)
|
||||||
addQueryParameter("since_id", sinceID);
|
addQueryParameter("since_id", sinceID);
|
||||||
|
if(GlobalUserPreferences.replyVisibility != null)
|
||||||
|
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
@ -20,5 +21,7 @@ public class GetPublicTimeline extends MastodonAPIRequest<List<Status>>{
|
||||||
addQueryParameter("max_id", maxID);
|
addQueryParameter("max_id", maxID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", limit+"");
|
addQueryParameter("limit", limit+"");
|
||||||
|
if(GlobalUserPreferences.replyVisibility != null)
|
||||||
|
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Markers;
|
import org.joinmastodon.android.model.Markers;
|
||||||
import org.joinmastodon.android.model.Preferences;
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
|
@ -87,4 +88,8 @@ public class AccountSession{
|
||||||
pushSubscriptionManager=new PushSubscriptionManager(getID());
|
pushSubscriptionManager=new PushSubscriptionManager(getID());
|
||||||
return pushSubscriptionManager;
|
return pushSubscriptionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Instance getInstance() {
|
||||||
|
return AccountSessionManager.getInstance().getInstanceInfo(domain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1087,7 +1087,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
req.status=text;
|
req.status=text;
|
||||||
req.localOnly=localOnly;
|
req.localOnly=localOnly;
|
||||||
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
req.visibility=localOnly && instance.isPleroma() ? StatusPrivacy.LOCAL : statusVisibility;
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
req.language=language;
|
req.language=language;
|
||||||
req.contentType=contentType;
|
req.contentType=contentType;
|
||||||
|
@ -1743,11 +1743,24 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
pollChanged=true;
|
pollChanged=true;
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
}));
|
}));
|
||||||
option.edit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0 ? instance.configuration.polls.maxCharactersPerOption : 50)});
|
|
||||||
|
int maxCharactersPerOption = 50;
|
||||||
|
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||||
|
maxCharactersPerOption = instance.configuration.polls.maxCharactersPerOption;
|
||||||
|
else if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||||
|
maxCharactersPerOption = instance.pollLimits.maxOptionChars;
|
||||||
|
option.edit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxCharactersPerOption)});
|
||||||
|
|
||||||
pollOptionsView.addView(option.view);
|
pollOptionsView.addView(option.view);
|
||||||
pollOptions.add(option);
|
pollOptions.add(option);
|
||||||
if(pollOptions.size()==(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0 ? instance.configuration.polls.maxOptions : 4))
|
|
||||||
|
int maxPollOptions = 4;
|
||||||
|
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||||
|
maxPollOptions = instance.configuration.polls.maxOptions;
|
||||||
|
else if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||||
|
maxPollOptions = instance.pollLimits.maxOptions;
|
||||||
|
|
||||||
|
if(pollOptions.size()==maxPollOptions)
|
||||||
addPollOptionBtn.setVisibility(View.GONE);
|
addPollOptionBtn.setVisibility(View.GONE);
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
@ -1889,7 +1902,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
Menu m=visibilityPopup.getMenu();
|
Menu m=visibilityPopup.getMenu();
|
||||||
MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only);
|
MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only);
|
||||||
boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
||||||
if (instance.pleroma != null) {
|
if (instance.isPleroma()) {
|
||||||
m.findItem(R.id.vis_local).setVisible(true);
|
m.findItem(R.id.vis_local).setVisible(true);
|
||||||
} else if (localOnly || prefsSaysSupported) {
|
} else if (localOnly || prefsSaysSupported) {
|
||||||
localOnlyItem.setVisible(true);
|
localOnlyItem.setVisible(true);
|
||||||
|
|
|
@ -30,8 +30,11 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
|
@ -164,7 +167,7 @@ public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition>
|
||||||
makeBackItem(listsMenu);
|
makeBackItem(listsMenu);
|
||||||
makeBackItem(hashtagsMenu);
|
makeBackItem(hashtagsMenu);
|
||||||
|
|
||||||
TimelineDefinition.ALL_TIMELINES.forEach(tl -> addTimelineToOptions(tl, timelinesMenu));
|
TimelineDefinition.getAllTimelines(accountID).forEach(tl -> addTimelineToOptions(tl, timelinesMenu));
|
||||||
listTimelines.stream().map(TimelineDefinition::ofList).forEach(tl -> addTimelineToOptions(tl, listsMenu));
|
listTimelines.stream().map(TimelineDefinition::ofList).forEach(tl -> addTimelineToOptions(tl, listsMenu));
|
||||||
hashtags.stream().map(TimelineDefinition::ofHashtag).forEach(tl -> addTimelineToOptions(tl, hashtagsMenu));
|
hashtags.stream().map(TimelineDefinition::ofHashtag).forEach(tl -> addTimelineToOptions(tl, hashtagsMenu));
|
||||||
|
|
||||||
|
@ -190,7 +193,7 @@ public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
onDataLoaded(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES), false);
|
onDataLoaded(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID)), false);
|
||||||
updateOptionsMenu();
|
updateOptionsMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||||
homeTabFragment=new HomeTabFragment();
|
homeTabFragment=new HomeTabFragment();
|
||||||
homeTabFragment.setArguments(args);
|
homeTabFragment.setArguments(args);
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
|
Instance instance = AccountSessionManager.getInstance().getAccount(accountID).getInstance();
|
||||||
|
args.putBoolean("isPleroma", instance.isPleroma());
|
||||||
args.putBoolean("noAutoLoad", true);
|
args.putBoolean("noAutoLoad", true);
|
||||||
searchFragment=new DiscoverFragment();
|
searchFragment=new DiscoverFragment();
|
||||||
searchFragment.setArguments(args);
|
searchFragment.setArguments(args);
|
||||||
|
@ -231,6 +233,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||||
if (newFragment instanceof HasFab fabulous) fabulous.showFab();
|
if (newFragment instanceof HasFab fabulous) fabulous.showFab();
|
||||||
currentTab=tab;
|
currentTab=tab;
|
||||||
((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this);
|
((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this);
|
||||||
|
if (tab == R.id.tab_search)
|
||||||
|
searchFragment.selectSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeTriggerLoading(Fragment newFragment){
|
private void maybeTriggerLoading(Fragment newFragment){
|
||||||
|
@ -290,10 +294,10 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||||
|
|
||||||
public void updateNotificationBadge() {
|
public void updateNotificationBadge() {
|
||||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
Instance instance = session.getInstance();
|
||||||
if (instance == null) return;
|
if (instance == null) return;
|
||||||
|
|
||||||
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance != null && instance.pleroma != null)
|
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance != null && instance.isPleroma())
|
||||||
.setCallback(new Callback<>() {
|
.setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Notification> notifications) {
|
public void onSuccess(List<Notification> notifications) {
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
E.register(this);
|
E.register(this);
|
||||||
accountID = getArguments().getString("account");
|
accountID = getArguments().getString("account");
|
||||||
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES);
|
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID));
|
||||||
assert timelineDefinitions != null;
|
assert timelineDefinitions != null;
|
||||||
if (timelineDefinitions.size() == 0) timelineDefinitions = List.of(TimelineDefinition.HOME_TIMELINE);
|
if (timelineDefinitions.size() == 0) timelineDefinitions = List.of(TimelineDefinition.HOME_TIMELINE);
|
||||||
count = timelineDefinitions.size();
|
count = timelineDefinitions.size();
|
||||||
|
|
|
@ -9,7 +9,10 @@ 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.MastodonErrorResponse;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
|
import org.joinmastodon.android.api.requests.notifications.PleromaMarkNotificationsRead;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
|
@ -18,6 +21,7 @@ import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
|
@ -40,6 +44,7 @@ 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.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||||
|
@ -158,6 +163,9 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).markers
|
AccountSessionManager.getInstance().getAccount(accountID).markers
|
||||||
.notifications.lastReadId = result.items.get(0).id;
|
.notifications.lastReadId = result.items.get(0).id;
|
||||||
AccountSessionManager.getInstance().writeAccountsFile();
|
AccountSessionManager.getInstance().writeAccountsFile();
|
||||||
|
|
||||||
|
if (AccountSessionManager.getInstance().getAccount(accountID).getInstance().isPleroma())
|
||||||
|
new PleromaMarkNotificationsRead(result.items.get(0).id).exec(accountID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,7 @@ public abstract class PinnableStatusListFragment extends StatusListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES));
|
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,7 +31,6 @@ import android.view.ViewOutlineProvider;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.view.animation.TranslateAnimation;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
@ -51,6 +50,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
||||||
|
@ -58,6 +58,7 @@ import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
|
@ -137,6 +138,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
private Account account;
|
private Account account;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
private String domain;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private int statusBarHeight;
|
private int statusBarHeight;
|
||||||
private boolean isOwnProfile;
|
private boolean isOwnProfile;
|
||||||
|
@ -151,7 +153,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
private PhotoViewer currentPhotoViewer;
|
private PhotoViewer currentPhotoViewer;
|
||||||
private boolean editModeLoading;
|
private boolean editModeLoading;
|
||||||
|
|
||||||
private static final int MAX_FIELDS=4;
|
private int maxFields = 4;
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
// from ProfileAboutFragment
|
||||||
public UsableRecyclerView list;
|
public UsableRecyclerView list;
|
||||||
|
@ -172,6 +174,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
|
domain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
if(getArguments().containsKey("profileAccount")){
|
if(getArguments().containsKey("profileAccount")){
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||||
profileAccountID=account.id;
|
profileAccountID=account.id;
|
||||||
|
@ -179,6 +182,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
loaded=true;
|
loaded=true;
|
||||||
if(!isOwnProfile)
|
if(!isOwnProfile)
|
||||||
loadRelationship();
|
loadRelationship();
|
||||||
|
else {
|
||||||
|
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(domain);
|
||||||
|
if (instance.isPleroma()) {
|
||||||
|
maxFields = instance.pleroma.metadata.fieldsLimits.maxFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
profileAccountID=getArguments().getString("profileAccountID");
|
profileAccountID=getArguments().getString("profileAccountID");
|
||||||
if(!getArguments().getBoolean("noAutoLoad", false))
|
if(!getArguments().getBoolean("noAutoLoad", false))
|
||||||
|
@ -324,7 +333,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
username.setOnLongClickListener(v->{
|
username.setOnLongClickListener(v->{
|
||||||
String usernameString=account.acct;
|
String usernameString=account.acct;
|
||||||
if(!usernameString.contains("@")){
|
if(!usernameString.contains("@")){
|
||||||
usernameString+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
usernameString+="@"+domain;
|
||||||
}
|
}
|
||||||
UiUtils.copyText(username, '@'+usernameString);
|
UiUtils.copyText(username, '@'+usernameString);
|
||||||
return true;
|
return true;
|
||||||
|
@ -510,7 +519,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
ssb.append(account.acct);
|
ssb.append(account.acct);
|
||||||
if(isSelf){
|
if(isSelf){
|
||||||
ssb.append('@');
|
ssb.append('@');
|
||||||
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
ssb.append(domain);
|
||||||
}
|
}
|
||||||
ssb.append(" ");
|
ssb.append(" ");
|
||||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
||||||
|
@ -520,7 +529,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
username.setText(ssb);
|
username.setText(ssb);
|
||||||
}else{
|
}else{
|
||||||
// noinspection SetTextI18n
|
// noinspection SetTextI18n
|
||||||
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
|
username.setText('@'+account.acct+(isSelf ? ('@'+domain) : ""));
|
||||||
}
|
}
|
||||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
if(TextUtils.isEmpty(parsedBio)){
|
if(TextUtils.isEmpty(parsedBio)){
|
||||||
|
@ -1189,7 +1198,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
if(isInEditMode){
|
if(isInEditMode){
|
||||||
int size=metadataListData.size();
|
int size=metadataListData.size();
|
||||||
if(size<MAX_FIELDS)
|
if(size<maxFields)
|
||||||
size++;
|
size++;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
@ -1306,7 +1315,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
metadataListData.add(new AccountField());
|
metadataListData.add(new AccountField());
|
||||||
if(metadataListData.size()==MAX_FIELDS){ // replace this row with new row
|
if(metadataListData.size()==maxFields){ // replace this row with new row
|
||||||
adapter.notifyItemChanged(metadataListData.size()-1);
|
adapter.notifyItemChanged(metadataListData.size()-1);
|
||||||
}else{
|
}else{
|
||||||
adapter.notifyItemInserted(metadataListData.size()-1);
|
adapter.notifyItemInserted(metadataListData.size()-1);
|
||||||
|
|
|
@ -105,7 +105,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
imageCache = ImageCache.getInstance(getActivity());
|
imageCache = ImageCache.getInstance(getActivity());
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
Instance instance = session.getInstance();
|
||||||
String instanceName = UiUtils.getInstanceName(accountID);
|
String instanceName = UiUtils.getInstanceName(accountID);
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
|
@ -223,7 +223,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
GlobalUserPreferences.showReplies=i.checked;
|
GlobalUserPreferences.showReplies=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
if (instance.pleroma != null) {
|
if (instance.isPleroma()) {
|
||||||
items.add(new ButtonItem(R.string.sk_settings_reply_visibility, R.drawable.ic_fluent_chat_24_regular, b->{
|
items.add(new ButtonItem(R.string.sk_settings_reply_visibility, R.drawable.ic_fluent_chat_24_regular, b->{
|
||||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||||
popupMenu.inflate(R.menu.reply_visibility);
|
popupMenu.inflate(R.menu.reply_visibility);
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class ThreadFragment extends StatusListFragment{
|
||||||
}
|
}
|
||||||
AccountSession account=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession account=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
||||||
if(instance.pleroma != null){
|
if(instance.isPleroma()){
|
||||||
List<String> threadIds=new ArrayList<>();
|
List<String> threadIds=new ArrayList<>();
|
||||||
threadIds.add(mainStatus.id);
|
threadIds.add(mainStatus.id);
|
||||||
for(Status s:result.descendants){
|
for(Status s:result.descendants){
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.joinmastodon.android.fragments.discover;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetBubbleTimeline;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
|
public class BubbleTimelineFragment extends StatusListFragment {
|
||||||
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.BUBBLE_TIMELINE);
|
||||||
|
private String maxID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean wantsComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
currentRequest=new GetBubbleTimeline(refreshing ? null : maxID, count)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
if(!result.isEmpty())
|
||||||
|
maxID=result.get(result.size()-1).id;
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||||
|
onDataLoaded(result, !result.isEmpty());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
bannerHelper.maybeAddBanner(contentWrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Filter.FilterContext getFilterContext() {
|
||||||
|
return Filter.FilterContext.PUBLIC;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.fragments.HomeFragment;
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
import org.joinmastodon.android.fragments.IsOnTop;
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
|
@ -238,7 +239,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||||
else scrollToTop();
|
else scrollToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectSearch() {
|
public void selectSearch() {
|
||||||
searchEdit.requestFocus();
|
searchEdit.requestFocus();
|
||||||
onSearchEditFocusChanged(searchEdit, true);
|
onSearchEditFocusChanged(searchEdit, true);
|
||||||
getActivity().getSystemService(InputMethodManager.class).showSoftInput(searchEdit, 0);
|
getActivity().getSystemService(InputMethodManager.class).showSoftInput(searchEdit, 0);
|
||||||
|
@ -272,6 +273,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||||
searchBack.setEnabled(false);
|
searchBack.setEnabled(false);
|
||||||
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(searchEdit.getWindowToken(), 0);
|
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(searchEdit.getWindowToken(), 0);
|
||||||
|
if (getArguments().getBoolean("isPleroma"))
|
||||||
|
((HomeFragment) getParentFragment()).onBackPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.joinmastodon.android.fragments.discover;
|
package org.joinmastodon.android.fragments.discover;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem.Holder.withHistoryParams;
|
||||||
|
import static org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem.Holder.withoutHistoryParams;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -108,9 +111,11 @@ public class TrendingHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
||||||
if (item.history == null || item.history.isEmpty()) {
|
if (item.history == null || item.history.isEmpty()) {
|
||||||
subtitle.setText(null);
|
subtitle.setText(null);
|
||||||
chart.setVisibility(View.GONE);
|
chart.setVisibility(View.GONE);
|
||||||
|
title.setLayoutParams(withoutHistoryParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chart.setVisibility(View.VISIBLE);
|
chart.setVisibility(View.VISIBLE);
|
||||||
|
title.setLayoutParams(withHistoryParams);
|
||||||
int numPeople=item.history.get(0).accounts;
|
int numPeople=item.history.get(0).accounts;
|
||||||
if(item.history.size()>1)
|
if(item.history.size()>1)
|
||||||
numPeople+=item.history.get(1).accounts;
|
numPeople+=item.history.get(1).accounts;
|
||||||
|
|
|
@ -86,6 +86,8 @@ public class Instance extends BaseModel{
|
||||||
|
|
||||||
public Pleroma pleroma;
|
public Pleroma pleroma;
|
||||||
|
|
||||||
|
public PleromaPollLimits pollLimits;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
super.postprocess();
|
super.postprocess();
|
||||||
|
@ -134,6 +136,10 @@ public class Instance extends BaseModel{
|
||||||
return ci;
|
return ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPleroma() {
|
||||||
|
return pleroma != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public static class Rule{
|
public static class Rule{
|
||||||
public String id;
|
public String id;
|
||||||
|
@ -198,6 +204,28 @@ public class Instance extends BaseModel{
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public static class Pleroma extends BaseModel {
|
public static class Pleroma extends BaseModel {
|
||||||
// metadata etc
|
public Pleroma.Metadata metadata;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class Metadata {
|
||||||
|
public List<String> features;
|
||||||
|
public Pleroma.Metadata.FieldsLimits fieldsLimits;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class FieldsLimits {
|
||||||
|
public int maxFields;
|
||||||
|
public int maxRemoteFields;
|
||||||
|
public int nameLength;
|
||||||
|
public int valueLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class PleromaPollLimits {
|
||||||
|
public int maxExpiration;
|
||||||
|
public int maxOptionChars;
|
||||||
|
public int maxOptions;
|
||||||
|
public int minExpiration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,20 @@ import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.discover.BubbleTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class TimelineDefinition {
|
public class TimelineDefinition {
|
||||||
private TimelineType type;
|
private TimelineType type;
|
||||||
|
@ -58,6 +61,14 @@ public class TimelineDefinition {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCompatible(AccountSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wantsDefault(AccountSession session) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTitle(Context ctx) {
|
public String getTitle(Context ctx) {
|
||||||
return title != null ? title : getDefaultTitle(ctx);
|
return title != null ? title : getDefaultTitle(ctx);
|
||||||
}
|
}
|
||||||
|
@ -78,6 +89,7 @@ public class TimelineDefinition {
|
||||||
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
||||||
case LIST -> listTitle;
|
case LIST -> listTitle;
|
||||||
case HASHTAG -> hashtagName;
|
case HASHTAG -> hashtagName;
|
||||||
|
case BUBBLE -> ctx.getString(R.string.sk_timeline_bubble);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +101,7 @@ public class TimelineDefinition {
|
||||||
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
||||||
case LIST -> Icon.LIST;
|
case LIST -> Icon.LIST;
|
||||||
case HASHTAG -> Icon.HASHTAG;
|
case HASHTAG -> Icon.HASHTAG;
|
||||||
|
case BUBBLE -> Icon.BUBBLE;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +113,7 @@ public class TimelineDefinition {
|
||||||
case LIST -> new ListTimelineFragment();
|
case LIST -> new ListTimelineFragment();
|
||||||
case HASHTAG -> new HashtagTimelineFragment();
|
case HASHTAG -> new HashtagTimelineFragment();
|
||||||
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
||||||
|
case BUBBLE -> new BubbleTimelineFragment();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +170,7 @@ public class TimelineDefinition {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TimelineType { HOME, LOCAL, FEDERATED, POST_NOTIFICATIONS, LIST, HASHTAG }
|
public enum TimelineType { HOME, LOCAL, FEDERATED, POST_NOTIFICATIONS, LIST, HASHTAG, BUBBLE }
|
||||||
|
|
||||||
public enum Icon {
|
public enum Icon {
|
||||||
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
||||||
|
@ -219,7 +233,8 @@ public class TimelineDefinition {
|
||||||
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
||||||
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
||||||
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
||||||
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true);
|
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true),
|
||||||
|
BUBBLE(R.drawable.ic_fluent_circle_24_regular, R.string.sk_timeline_bubble, true);
|
||||||
|
|
||||||
public final int iconRes, nameRes;
|
public final int iconRes, nameRes;
|
||||||
public final boolean hidden;
|
public final boolean hidden;
|
||||||
|
@ -239,14 +254,49 @@ public class TimelineDefinition {
|
||||||
public static final TimelineDefinition LOCAL_TIMELINE = new TimelineDefinition(TimelineType.LOCAL);
|
public static final TimelineDefinition LOCAL_TIMELINE = new TimelineDefinition(TimelineType.LOCAL);
|
||||||
public static final TimelineDefinition FEDERATED_TIMELINE = new TimelineDefinition(TimelineType.FEDERATED);
|
public static final TimelineDefinition FEDERATED_TIMELINE = new TimelineDefinition(TimelineType.FEDERATED);
|
||||||
public static final TimelineDefinition POSTS_TIMELINE = new TimelineDefinition(TimelineType.POST_NOTIFICATIONS);
|
public static final TimelineDefinition POSTS_TIMELINE = new TimelineDefinition(TimelineType.POST_NOTIFICATIONS);
|
||||||
|
public static final TimelineDefinition BUBBLE_TIMELINE = new TimelineDefinition(TimelineType.BUBBLE) {
|
||||||
|
@Override
|
||||||
|
public boolean isCompatible(AccountSession session) {
|
||||||
|
// still enabling the bubble timeline for all pleroma/akkoma instances since i know of
|
||||||
|
// at least one instance that supports it, but doesn't list "bubble_timeline"
|
||||||
|
return session.getInstance().isPleroma();
|
||||||
|
}
|
||||||
|
|
||||||
public static final List<TimelineDefinition> DEFAULT_TIMELINES = BuildConfig.BUILD_TYPE.equals("playRelease")
|
@Override
|
||||||
? List.of(HOME_TIMELINE.copy(), LOCAL_TIMELINE.copy())
|
public boolean wantsDefault(AccountSession session) {
|
||||||
: List.of(HOME_TIMELINE.copy(), LOCAL_TIMELINE.copy(), FEDERATED_TIMELINE.copy());
|
Instance instance = session.getInstance();
|
||||||
public static final List<TimelineDefinition> ALL_TIMELINES = List.of(
|
return instance.isPleroma() && instance.pleroma.metadata.features.contains("bubble_timeline");
|
||||||
HOME_TIMELINE.copy(),
|
}
|
||||||
LOCAL_TIMELINE.copy(),
|
};
|
||||||
FEDERATED_TIMELINE.copy(),
|
|
||||||
POSTS_TIMELINE.copy()
|
public static List<TimelineDefinition> getDefaultTimelines(String accountId) {
|
||||||
|
AccountSession session = AccountSessionManager.getInstance().getAccount(accountId);
|
||||||
|
return DEFAULT_TIMELINES.stream()
|
||||||
|
.filter(tl -> tl.isCompatible(session) && tl.wantsDefault(session))
|
||||||
|
.map(TimelineDefinition::copy)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TimelineDefinition> getAllTimelines(String accountId) {
|
||||||
|
AccountSession session = AccountSessionManager.getInstance().getAccount(accountId);
|
||||||
|
return ALL_TIMELINES.stream()
|
||||||
|
.filter(tl -> tl.isCompatible(session))
|
||||||
|
.map(TimelineDefinition::copy)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<TimelineDefinition> DEFAULT_TIMELINES = List.of(
|
||||||
|
HOME_TIMELINE,
|
||||||
|
LOCAL_TIMELINE,
|
||||||
|
BUBBLE_TIMELINE,
|
||||||
|
FEDERATED_TIMELINE
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<TimelineDefinition> ALL_TIMELINES = List.of(
|
||||||
|
HOME_TIMELINE,
|
||||||
|
LOCAL_TIMELINE,
|
||||||
|
FEDERATED_TIMELINE,
|
||||||
|
POSTS_TIMELINE,
|
||||||
|
BUBBLE_TIMELINE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.joinmastodon.android.ui.displayitems;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
@ -26,6 +27,13 @@ public class HashtagStatusDisplayItem extends StatusDisplayItem{
|
||||||
public static class Holder extends StatusDisplayItem.Holder<HashtagStatusDisplayItem>{
|
public static class Holder extends StatusDisplayItem.Holder<HashtagStatusDisplayItem>{
|
||||||
private final TextView title, subtitle;
|
private final TextView title, subtitle;
|
||||||
private final HashtagChartView chart;
|
private final HashtagChartView chart;
|
||||||
|
public static final RelativeLayout.LayoutParams
|
||||||
|
withHistoryParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT),
|
||||||
|
withoutHistoryParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
|
||||||
|
static {
|
||||||
|
withoutHistoryParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
public Holder(Context context, ViewGroup parent){
|
public Holder(Context context, ViewGroup parent){
|
||||||
super(context, R.layout.item_trending_hashtag, parent);
|
super(context, R.layout.item_trending_hashtag, parent);
|
||||||
|
@ -41,9 +49,11 @@ public class HashtagStatusDisplayItem extends StatusDisplayItem{
|
||||||
if (item.history == null || item.history.isEmpty()) {
|
if (item.history == null || item.history.isEmpty()) {
|
||||||
subtitle.setText(null);
|
subtitle.setText(null);
|
||||||
chart.setVisibility(View.GONE);
|
chart.setVisibility(View.GONE);
|
||||||
|
title.setLayoutParams(withoutHistoryParams);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chart.setVisibility(View.VISIBLE);
|
chart.setVisibility(View.VISIBLE);
|
||||||
|
title.setLayoutParams(withHistoryParams);
|
||||||
int numPeople=item.history.get(0).accounts;
|
int numPeople=item.history.get(0).accounts;
|
||||||
if(item.history.size()>1)
|
if(item.history.size()>1)
|
||||||
numPeople+=item.history.get(1).accounts;
|
numPeople+=item.history.get(1).accounts;
|
||||||
|
|
|
@ -166,6 +166,15 @@ public abstract class StatusDisplayItem{
|
||||||
items.add(replyLine);
|
items.add(replyLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statusForContent.quote != null) {
|
||||||
|
boolean hasQuoteInlineTag = statusForContent.content.contains("<span class=\"quote-inline\">");
|
||||||
|
if (!hasQuoteInlineTag) {
|
||||||
|
String quoteUrl = statusForContent.quote.url;
|
||||||
|
String quoteInline = String.format("<span class=\"quote-inline\">%sRE: <a href=\"%s\">%s</a></span>",
|
||||||
|
statusForContent.content.endsWith("</p>") ? "" : "<br/><br/>", quoteUrl, quoteUrl);
|
||||||
|
statusForContent.content += quoteInline;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(!TextUtils.isEmpty(statusForContent.content))
|
if(!TextUtils.isEmpty(statusForContent.content))
|
||||||
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, disableTranslate));
|
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, disableTranslate));
|
||||||
else if (!GlobalUserPreferences.replyLineAboveHeader && replyLine != null)
|
else if (!GlobalUserPreferences.replyLineAboveHeader && replyLine != null)
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class DiscoverInfoBannerHelper{
|
||||||
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
|
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
|
||||||
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
|
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
|
||||||
case POST_NOTIFICATIONS -> R.string.sk_notify_posts_info_banner;
|
case POST_NOTIFICATIONS -> R.string.sk_notify_posts_info_banner;
|
||||||
|
case BUBBLE_TIMELINE -> R.string.sk_bubble_timeline_info_banner;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +64,7 @@ public class DiscoverInfoBannerHelper{
|
||||||
LOCAL_TIMELINE,
|
LOCAL_TIMELINE,
|
||||||
FEDERATED_TIMELINE,
|
FEDERATED_TIMELINE,
|
||||||
POST_NOTIFICATIONS,
|
POST_NOTIFICATIONS,
|
||||||
// ACCOUNTS
|
// ACCOUNTS,
|
||||||
|
BUBBLE_TIMELINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
@ -946,7 +947,7 @@ public class UiUtils {
|
||||||
|
|
||||||
public static String getInstanceName(String accountID) {
|
public static String getInstanceName(String accountID) {
|
||||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
Instance instance = session.getInstance();
|
||||||
return instance != null && !instance.title.isBlank() ? instance.title : session.domain;
|
return instance != null && !instance.title.isBlank() ? instance.title : session.domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,14 +1113,20 @@ public class UiUtils {
|
||||||
if (!results.statuses.isEmpty()) {
|
if (!results.statuses.isEmpty()) {
|
||||||
args.putParcelable("status", Parcels.wrap(results.statuses.get(0)));
|
args.putParcelable("status", Parcels.wrap(results.statuses.get(0)));
|
||||||
Nav.go((Activity) context, ThreadFragment.class, args);
|
Nav.go((Activity) context, ThreadFragment.class, args);
|
||||||
} else if (!results.accounts.isEmpty()) {
|
return;
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(results.accounts.get(0)));
|
|
||||||
Nav.go((Activity) context, ProfileFragment.class, args);
|
|
||||||
} else {
|
|
||||||
if (launchBrowser) launchWebBrowser(context, url);
|
|
||||||
else
|
|
||||||
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
|
Optional<Account> account = results.accounts.stream()
|
||||||
|
.filter(a -> uri.equals(Uri.parse(a.url))).findAny();
|
||||||
|
if (account.isPresent()) {
|
||||||
|
args.putParcelable("profileAccount", Parcels.wrap(account.get()));
|
||||||
|
Nav.go((Activity) context, ProfileFragment.class, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (launchBrowser) {
|
||||||
|
launchWebBrowser(context, url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<string name="sk_user_post_notifications_off">Turned off post notifications for %s</string>
|
<string name="sk_user_post_notifications_off">Turned off post notifications for %s</string>
|
||||||
<string name="sk_federated_timeline">Federation</string>
|
<string name="sk_federated_timeline">Federation</string>
|
||||||
<string name="sk_federated_timeline_info_banner">These are the most recent posts by the people in your federation.</string>
|
<string name="sk_federated_timeline_info_banner">These are the most recent posts by the people in your federation.</string>
|
||||||
|
<string name="sk_bubble_timeline_info_banner">These are the most recent posts by the people in your Akkoma server\'s bubble.</string>
|
||||||
<string name="sk_update_available">Megalodon %s is ready to download.</string>
|
<string name="sk_update_available">Megalodon %s is ready to download.</string>
|
||||||
<string name="sk_update_ready">Megalodon %s is downloaded and ready to install.</string>
|
<string name="sk_update_ready">Megalodon %s is downloaded and ready to install.</string>
|
||||||
<string name="sk_check_for_update">Check for update</string>
|
<string name="sk_check_for_update">Check for update</string>
|
||||||
|
@ -148,6 +149,7 @@
|
||||||
<string name="sk_timeline_home">Home</string>
|
<string name="sk_timeline_home">Home</string>
|
||||||
<string name="sk_timeline_local">Local</string>
|
<string name="sk_timeline_local">Local</string>
|
||||||
<string name="sk_timeline_federated">Federation</string>
|
<string name="sk_timeline_federated">Federation</string>
|
||||||
|
<string name="sk_timeline_bubble">Bubble</string>
|
||||||
<string name="sk_recent_searches_placeholder">Type to start searching</string>
|
<string name="sk_recent_searches_placeholder">Type to start searching</string>
|
||||||
<string name="sk_remove_follower">Remove as follower</string>
|
<string name="sk_remove_follower">Remove as follower</string>
|
||||||
<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>
|
||||||
|
|
Loading…
Reference in New Issue