Merge remote-tracking branch 'megalodon_main/main'
# Conflicts: # mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java # mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ComposePollViewController.java # metadata/uk/changelogs/56.txt
This commit is contained in:
@@ -551,10 +551,14 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
protected void updatePoll(String itemID, Status status, Poll poll){
|
||||
status.poll=poll;
|
||||
int firstOptionIndex=-1, footerIndex=-1;
|
||||
int spoilerFirstOptionIndex=-1, spoilerFooterIndex=-1;
|
||||
SpoilerStatusDisplayItem spoilerItem=null;
|
||||
int i=0;
|
||||
for(StatusDisplayItem item:displayItems){
|
||||
if(item.parentID.equals(itemID)){
|
||||
if(item instanceof PollOptionStatusDisplayItem && firstOptionIndex==-1){
|
||||
if(item instanceof SpoilerStatusDisplayItem){
|
||||
spoilerItem=(SpoilerStatusDisplayItem) item;
|
||||
}else if(item instanceof PollOptionStatusDisplayItem && firstOptionIndex==-1){
|
||||
firstOptionIndex=i;
|
||||
}else if(item instanceof PollFooterStatusDisplayItem){
|
||||
footerIndex=i;
|
||||
@@ -567,8 +571,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
throw new IllegalStateException("Can't find all poll items in displayItems");
|
||||
List<StatusDisplayItem> pollItems=displayItems.subList(firstOptionIndex, footerIndex+1);
|
||||
int prevSize=pollItems.size();
|
||||
if(spoilerItem!=null){
|
||||
spoilerFirstOptionIndex=spoilerItem.contentItems.indexOf(pollItems.get(0));
|
||||
spoilerFooterIndex=spoilerItem.contentItems.indexOf(pollItems.get(pollItems.size()-1));
|
||||
}
|
||||
pollItems.clear();
|
||||
StatusDisplayItem.buildPollItems(itemID, this, poll, pollItems, status);
|
||||
if(spoilerItem!=null){
|
||||
spoilerItem.contentItems.subList(spoilerFirstOptionIndex, spoilerFooterIndex+1).clear();
|
||||
spoilerItem.contentItems.addAll(spoilerFirstOptionIndex, pollItems);
|
||||
}
|
||||
if(prevSize!=pollItems.size()){
|
||||
adapter.notifyItemRangeRemoved(firstOptionIndex, prevSize);
|
||||
adapter.notifyItemRangeInserted(firstOptionIndex, pollItems.size());
|
||||
|
||||
@@ -215,7 +215,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
public Instance instance;
|
||||
|
||||
public Status editingStatus;
|
||||
private ScheduledStatus scheduledStatus;
|
||||
public ScheduledStatus scheduledStatus;
|
||||
private boolean redraftStatus;
|
||||
|
||||
private Uri photoUri;
|
||||
@@ -758,7 +758,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
||||
}
|
||||
|
||||
replyText.setText(getString(quote!=null? R.string.sk_quoting_user : R.string.in_reply_to, status.account.getDisplayName()));
|
||||
replyText.setText(HtmlParser.parseCustomEmoji(getString(quote!=null? R.string.sk_quoting_user : R.string.in_reply_to, status.account.getDisplayName()), status.account.emojis));
|
||||
UiUtils.loadCustomEmojiInTextView(replyText);
|
||||
int visibilityNameRes = switch (status.visibility) {
|
||||
case PUBLIC -> R.string.visibility_public;
|
||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
||||
|
||||
@@ -8,10 +8,14 @@ import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.FilterContext;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
@@ -23,9 +27,11 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
@@ -51,6 +57,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
String maxID=this.maxID;
|
||||
AccountSessionManager.getInstance()
|
||||
.getAccount(accountID).getCacheController()
|
||||
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||
@@ -58,7 +65,8 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||
if(getActivity()==null) return;
|
||||
boolean empty=result.items.isEmpty();
|
||||
maxID=result.maxID;
|
||||
if(result.isFromCache()) refreshCachedData(result, maxID);
|
||||
HomeTimelineFragment.this.maxID=result.maxID;
|
||||
AccountSessionManager.get(accountID).filterStatuses(result.items, getFilterContext());
|
||||
onDataLoaded(result.items, !empty);
|
||||
if(result.isFromCache() && GlobalUserPreferences.loadNewPosts)
|
||||
@@ -67,6 +75,53 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
});
|
||||
}
|
||||
|
||||
private void handleRefreshedData(List<Status> result, List<Status> cachedList){
|
||||
Map<String, Status> refreshed=result.stream().collect(Collectors.toMap(Status::getID, Function.identity()));
|
||||
for(Status cached : cachedList){
|
||||
if(refreshed.containsKey(cached.id)){
|
||||
Status updated=refreshed.get(cached.id);
|
||||
if(updated.editedAt!=null && cached.editedAt!=null && updated.editedAt.isAfter(cached.editedAt))
|
||||
E.post(new StatusUpdatedEvent(updated));
|
||||
else
|
||||
E.post(new StatusCountersUpdatedEvent(updated.getContentStatus()));
|
||||
}else{
|
||||
removeStatus(cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshCachedData(CacheablePaginatedResponse<List<Status>> result, String maxID){
|
||||
List<Status> cachedList=new ArrayList<>(result.items);
|
||||
if(cachedList.isEmpty()) return;
|
||||
if(maxID==null){
|
||||
// fetch top status manually so we can use its id as the max_id to fetch the rest
|
||||
Status firstFromCache=cachedList.get(0);
|
||||
maxID=firstFromCache.id;
|
||||
cachedList.remove(0);
|
||||
new GetStatusByID(maxID).setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Status result){
|
||||
handleRefreshedData(
|
||||
Collections.singletonList(result),
|
||||
Collections.singletonList(firstFromCache)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse ignored){}
|
||||
}).exec(accountID);
|
||||
}
|
||||
new GetHomeTimeline(maxID, null, cachedList.size(), null, getSession().getLocalPreferences().timelineReplyVisibility).setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
handleRefreshedData(result, cachedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse ignored){}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.joinmastodon.android.model.Poll.Option;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -27,10 +28,10 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException {
|
||||
super.postprocess();
|
||||
if (mediaAttachments == null) mediaAttachments = List.of();
|
||||
if(mediaAttachments==null) mediaAttachments=List.of();
|
||||
for(Attachment a:mediaAttachments)
|
||||
a.postprocess();
|
||||
if (params != null) params.postprocess();
|
||||
if(params!=null) params.postprocess();
|
||||
}
|
||||
|
||||
@Parcel
|
||||
@@ -53,7 +54,7 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException {
|
||||
super.postprocess();
|
||||
if (poll != null) poll.postprocess();
|
||||
if(poll!=null) poll.postprocess();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,25 +68,26 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
||||
public boolean hideTotals;
|
||||
|
||||
public Poll toPoll() {
|
||||
Poll p = new Poll();
|
||||
p.voted = true;
|
||||
p.emojis = List.of();
|
||||
p.ownVotes = List.of();
|
||||
p.multiple = multiple;
|
||||
p.options = options.stream().map(Option::new).collect(Collectors.toList());
|
||||
Poll p=new Poll();
|
||||
p.voted=true;
|
||||
p.emojis=List.of();
|
||||
p.ownVotes=List.of();
|
||||
p.multiple=multiple;
|
||||
p.options=options.stream().map(Option::new).collect(Collectors.toList());
|
||||
p.expiresAt=Instant.now().plus(Integer.parseInt(expiresIn)+1, ChronoUnit.SECONDS);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
public Status toStatus() {
|
||||
Status s = Status.ofFake(id, params.text, scheduledAt);
|
||||
s.mediaAttachments = mediaAttachments;
|
||||
s.inReplyToId = params.inReplyToId > 0 ? "" + params.inReplyToId : null;
|
||||
s.spoilerText = params.spoilerText;
|
||||
s.visibility = params.visibility;
|
||||
s.language = params.language;
|
||||
s.sensitive = params.sensitive;
|
||||
if (params.poll != null) s.poll = params.poll.toPoll();
|
||||
Status s=Status.ofFake(id, params.text, scheduledAt);
|
||||
s.mediaAttachments=mediaAttachments;
|
||||
s.inReplyToId=params.inReplyToId>0 ? ""+params.inReplyToId : null;
|
||||
s.spoilerText=params.spoilerText;
|
||||
s.visibility=params.visibility;
|
||||
s.language=params.language;
|
||||
s.sensitive=params.sensitive;
|
||||
if(params.poll!=null) s.poll=params.poll.toPoll();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,7 +566,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
follow.setIcon(following ? R.drawable.ic_fluent_person_delete_24_regular : R.drawable.ic_fluent_person_add_24_regular);
|
||||
manageUserLists.setVisible(relationship != null && relationship.following);
|
||||
manageUserLists.setTitle(item.parentFragment.getString(R.string.sk_lists_with_user, username));
|
||||
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow);
|
||||
// ic_fluent_person_add_24_regular actually has a width of 25dp -.-
|
||||
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow, following ? 0 : V.dp(-1));
|
||||
}
|
||||
|
||||
workaroundChangingMenuItemWidths(menu, username);
|
||||
|
||||
@@ -6,6 +6,7 @@ import static org.joinmastodon.android.ui.utils.UiUtils.generateFormattedString;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
@@ -143,6 +144,8 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
item.emojiHelper.setImageDrawable(index-1, image);
|
||||
text.invalidate();
|
||||
}
|
||||
if(image instanceof Animatable)
|
||||
((Animatable) image).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -814,8 +814,8 @@ public class UiUtils {
|
||||
button.setText(relationship.followedBy ? R.string.follow_back : R.string.button_follow);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Filled;
|
||||
}else{
|
||||
button.setText(R.string.button_following);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||
button.setText(relationship.followedBy ? R.string.sk_button_mutuals : R.string.button_following);
|
||||
styleRes=relationship.followedBy ? R.style.Widget_Mastodon_M3_Button_Tonal_Outlined : R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||
}
|
||||
|
||||
TypedArray ta=button.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
||||
@@ -941,17 +941,26 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static void insetPopupMenuIcon(Context context, MenuItem item) {
|
||||
ColorStateList iconTint = ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
|
||||
insetPopupMenuIcon(item, iconTint);
|
||||
insetPopupMenuIcon(context, item, 0);
|
||||
}
|
||||
|
||||
public static void insetPopupMenuIcon(MenuItem item, ColorStateList iconTint) {
|
||||
Drawable icon = item.getIcon().mutate();
|
||||
if (Build.VERSION.SDK_INT >= 26) item.setIconTintList(iconTint);
|
||||
public static void insetPopupMenuIcon(Context context, MenuItem item, int addWidth) {
|
||||
ColorStateList iconTint = ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
|
||||
insetPopupMenuIcon(item, iconTint, addWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param addWidth set if icon is too wide/narrow. if icon is 25dp in width, set to -1dp
|
||||
*/
|
||||
public static void insetPopupMenuIcon(MenuItem item, ColorStateList iconTint, int addWidth) {
|
||||
Drawable icon=item.getIcon().mutate();
|
||||
if(Build.VERSION.SDK_INT>=26) item.setIconTintList(iconTint);
|
||||
else icon.setTintList(iconTint);
|
||||
icon = new InsetDrawable(icon, V.dp(8), 0, V.dp(8), 0);
|
||||
int pad=V.dp(8);
|
||||
boolean rtl=icon.getLayoutDirection()==View.LAYOUT_DIRECTION_RTL;
|
||||
icon=new InsetDrawable(icon, rtl ? pad+addWidth : pad, 0, rtl ? pad : addWidth+pad, 0);
|
||||
item.setIcon(icon);
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder(item.getTitle());
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder(item.getTitle());
|
||||
item.setTitle(ssb);
|
||||
}
|
||||
|
||||
@@ -985,7 +994,7 @@ public class UiUtils {
|
||||
if (subMenu != null) enableMenuIcons(context, subMenu, exclude);
|
||||
if (item.getIcon() == null || Arrays.stream(exclude).anyMatch(id -> id == item.getItemId()))
|
||||
continue;
|
||||
insetPopupMenuIcon(item, iconTint);
|
||||
insetPopupMenuIcon(item, iconTint, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,9 @@ public class ComposePollViewController{
|
||||
DraftPollOption opt=createDraftPollOption(false);
|
||||
opt.edit.setText(eopt.title);
|
||||
}
|
||||
if(fragment.editingStatus.poll.expiresAt!=null)
|
||||
if(fragment.scheduledStatus!=null && fragment.scheduledStatus.params.poll!=null)
|
||||
pollDuration=Integer.parseInt(fragment.scheduledStatus.params.poll.expiresIn);
|
||||
else if(fragment.editingStatus.poll.expiresAt!=null)
|
||||
pollDuration=(int)fragment.editingStatus.poll.expiresAt.minus(fragment.editingStatus.createdAt.toEpochMilli(), ChronoUnit.MILLIS).getEpochSecond();
|
||||
updatePollOptionHints();
|
||||
pollDurationValue.setText(UiUtils.formatDuration(fragment.getContext(), pollDuration));
|
||||
|
||||
Reference in New Issue
Block a user