Thread view

This commit is contained in:
Grishka 2022-02-11 18:16:56 +03:00
parent eed64f48fe
commit 3e605253e9
12 changed files with 185 additions and 14 deletions

View File

@ -0,0 +1,10 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.StatusContext;
public class GetStatusContext extends MastodonAPIRequest<StatusContext>{
public GetStatusContext(String id){
super(HttpMethod.GET, "/statuses/"+id+"/context", StatusContext.class);
}
}

View File

@ -40,8 +40,6 @@ public class AccountTimelineFragment extends StatusListFragment{
super.onAttach(activity);
user=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
filter=GetAccountStatuses.Filter.valueOf(getArguments().getString("filter"));
if(!getArguments().getBoolean("noAutoLoad"))
loadData();
}
@Override
@ -60,4 +58,11 @@ public class AccountTimelineFragment extends StatusListFragment{
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
}

View File

@ -335,14 +335,14 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
return displayItems.get(position).getImageRequest(image);
}
@Override
public void onViewDetachedFromWindow(@NonNull BindableViewHolder<StatusDisplayItem> holder){
if(holder instanceof ImageLoaderViewHolder){
int count=holder.getItem().getImageCount();
for(int i=0;i<count;i++){
((ImageLoaderViewHolder) holder).clearImage(i);
}
}
}
// @Override
// public void onViewDetachedFromWindow(@NonNull BindableViewHolder<StatusDisplayItem> holder){
// if(holder instanceof ImageLoaderViewHolder){
// int count=holder.getItem().getImageCount();
// for(int i=0;i<count;i++){
// ((ImageLoaderViewHolder) holder).clearImage(i);
// }
// }
// }
}
}

View File

@ -54,6 +54,7 @@ import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.CustomEmojiPopupKeyboard;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.PopupKeyboard;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
import org.parceler.Parcels;
@ -150,7 +151,7 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis
selfName=view.findViewById(R.id.name);
selfUsername=view.findViewById(R.id.username);
selfAvatar=view.findViewById(R.id.avatar);
selfName.setText(self.displayName);
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
selfUsername.setText('@'+self.username+'@'+instanceDomain);
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){

View File

@ -80,6 +80,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
args.putString("account", accountID);
homeTimelineFragment=new HomeTimelineFragment();
homeTimelineFragment.setArguments(args);
args=new Bundle(args);
args.putBoolean("noAutoLoad", true);
searchFragment=new SearchFragment();
searchFragment.setArguments(args);
notificationsFragment=new NotificationsFragment();
@ -150,7 +152,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
if(newFragment instanceof LoaderFragment){
LoaderFragment lf=(LoaderFragment) newFragment;
if(!lf.loaded)
if(!lf.loaded && !lf.dataLoading)
lf.loadData();
}
currentTab=tab;

View File

@ -45,7 +45,7 @@ public class HomeTimelineFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
new GetHomeTimeline(offset>0 ? getMaxID() : null, null, count)
currentRequest=new GetHomeTimeline(offset>0 ? getMaxID() : null, null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
@ -90,6 +90,13 @@ public class HomeTimelineFragment extends StatusListFragment{
updateToolbarLogo();
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
@Subscribe
public void onStatusCreated(StatusCreatedEvent ev){
prependItems(Collections.singletonList(ev.status));

View File

@ -60,4 +60,11 @@ public class NotificationsFragment extends BaseStatusListFragment<Notification>{
})
.exec(accountID);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
}

View File

@ -9,10 +9,12 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.parceler.Parcels;
import java.util.List;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
protected List<StatusDisplayItem> buildDisplayItems(Status s){
@ -37,6 +39,25 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
E.unregister(this);
}
@Override
public void onItemClick(String id){
Status status=null;
for(Status s:data){
if(s.id.equals(id)){
status=s.getContentStatus();
break;
}
}
if(status==null)
return;
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("status", Parcels.wrap(status));
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
Nav.go(getActivity(), ThreadFragment.class, args);
}
@Subscribe
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
for(Status s:data){

View File

@ -0,0 +1,79 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.StatusContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.util.Collections;
import me.grishka.appkit.api.SimpleCallback;
public class ThreadFragment extends StatusListFragment{
private Status mainStatus;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
mainStatus=Parcels.unwrap(getArguments().getParcelable("status"));
Account inReplyToAccount=Parcels.unwrap(getArguments().getParcelable("inReplyToAccount"));
if(inReplyToAccount!=null)
knownAccounts.put(inReplyToAccount.id, inReplyToAccount);
setTitle(HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.displayName), mainStatus.account.emojis));
data.add(mainStatus);
onAppendItems(Collections.singletonList(mainStatus));
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetStatusContext(mainStatus.id)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(StatusContext result){
if(refreshing){
data.clear();
displayItems.clear();
data.add(mainStatus);
onAppendItems(Collections.singletonList(mainStatus));
}
footerProgress.setVisibility(View.GONE);
data.addAll(result.descendants);
onAppendItems(result.descendants);
int count=displayItems.size();
prependItems(result.ancestors);
dataLoaded();
if(refreshing)
refreshDone();
list.scrollToPosition(displayItems.size()-count);
}
})
.exec(accountID);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading){
dataLoading=true;
doLoadData();
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
UiUtils.loadCustomEmojiInTextView(toolbarTitleView);
showContent();
if(!loaded)
footerProgress.setVisibility(View.VISIBLE);
}
}

View File

@ -0,0 +1,21 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.api.ObjectValidationException;
import java.util.List;
@AllFieldsAreRequired
public class StatusContext extends BaseModel{
public List<Status> ancestors;
public List<Status> descendants;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
for(Status s:ancestors)
s.postprocess();
for(Status s:descendants)
s.postprocess();
}
}

View File

@ -2,8 +2,10 @@ package org.joinmastodon.android.ui.text;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.widget.TextView;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
@ -117,4 +119,19 @@ public class HtmlParser{
ssb.setSpan(new CustomEmojiSpan(emoji), matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
public static SpannableStringBuilder parseCustomEmoji(String text, List<Emoji> emojis){
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
parseCustomEmoji(ssb, emojis);
return ssb;
}
public static void setTextWithCustomEmoji(TextView view, String text, List<Emoji> emojis){
if(!EMOJI_CODE_PATTERN.matcher(text).find()){
view.setText(text);
return;
}
view.setText(parseCustomEmoji(text, emojis));
UiUtils.loadCustomEmojiInTextView(view);
}
}

View File

@ -72,4 +72,5 @@
<string name="field_label">Label</string>
<string name="field_content">Content</string>
<string name="saving">Saving…</string>
<string name="post_from_user">Post from %s</string>
</resources>