Merge pull request #429 from FineFindus/feat/trending-links-timeline
feat(Discover): add Timeline to trending links
This commit is contained in:
commit
0bdb23e462
|
@ -0,0 +1,23 @@
|
||||||
|
package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetTrendingLinksTimeline extends MastodonAPIRequest<List<Status>>{
|
||||||
|
public GetTrendingLinksTimeline(@NonNull String url, String maxID, String minID, int limit){
|
||||||
|
super(HttpMethod.GET, "/timelines/link/", new TypeToken<>(){});
|
||||||
|
addQueryParameter("url", url);
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(minID!=null)
|
||||||
|
addQueryParameter("min_id", minID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", ""+limit);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
|
||||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -29,6 +30,7 @@ import java.util.stream.Collectors;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
|
@ -215,7 +217,16 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<CardViewModel> im
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
|
//TODO: enable timeline for all servers once 4.3.0 is released
|
||||||
|
if(getInstance().isEmpty() ||
|
||||||
|
!getInstance().get().version.contains("4.3.0")){
|
||||||
UiUtils.launchWebBrowser(getActivity(), item.url);
|
UiUtils.launchWebBrowser(getActivity(), item.url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putParcelable("trendingLink", Parcels.wrap(item));
|
||||||
|
Nav.go(getActivity(), DiscoverTrendingLinkTimelineFragment.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
package org.joinmastodon.android.fragments.discover;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetTrendingLinksTimeline;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
|
import org.joinmastodon.android.fragments.HomeTabFragment;
|
||||||
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
|
import org.joinmastodon.android.model.Card;
|
||||||
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
|
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
//TODO: replace this implementation when upstream implements their own design
|
||||||
|
public class DiscoverTrendingLinkTimelineFragment extends StatusListFragment{
|
||||||
|
private Card trendingLink;
|
||||||
|
private TextView headerTitle, headerSubtitle;
|
||||||
|
private Button openLinkButton;
|
||||||
|
private boolean toolbarContentVisible;
|
||||||
|
|
||||||
|
private Menu optionsMenu;
|
||||||
|
private MenuInflater optionsMenuInflater;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean wantsComposeButton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
trendingLink=Parcels.unwrap(getArguments().getParcelable("trendingLink"));
|
||||||
|
setTitle(trendingLink.title);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
currentRequest=new GetTrendingLinksTimeline(trendingLink.url, getMaxID(), null, count)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
if(getActivity()==null) return;
|
||||||
|
boolean more=applyMaxID(result);
|
||||||
|
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
||||||
|
onDataLoaded(result, more);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
|
||||||
|
if(getParentFragment() instanceof HomeTabFragment) return;
|
||||||
|
|
||||||
|
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
|
View topChild=recyclerView.getChildAt(0);
|
||||||
|
int firstChildPos=recyclerView.getChildAdapterPosition(topChild);
|
||||||
|
float newAlpha=firstChildPos>0 ? 1f : Math.min(1f, -topChild.getTop()/(float)headerTitle.getHeight());
|
||||||
|
toolbarTitleView.setAlpha(newAlpha);
|
||||||
|
boolean newToolbarVisibility=newAlpha>0.5f;
|
||||||
|
if(newToolbarVisibility!=toolbarContentVisible){
|
||||||
|
toolbarContentVisible=newToolbarVisibility;
|
||||||
|
createOptionsMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFabLongClick(View v) {
|
||||||
|
return UiUtils.pickAccountForCompose(getActivity(), accountID, trendingLink.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFabClick(View v){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("prefilledText", trendingLink.url);
|
||||||
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSetFabBottomInset(int inset){
|
||||||
|
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16)+inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FilterContext getFilterContext() {
|
||||||
|
return FilterContext.PUBLIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri getWebUri(Uri.Builder base) {
|
||||||
|
//TODO: add URL link once web version implements a UI
|
||||||
|
return base.path("/explore/links").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
|
View header=getActivity().getLayoutInflater().inflate(R.layout.header_trending_link_timeline, list, false);
|
||||||
|
headerTitle=header.findViewById(R.id.title);
|
||||||
|
headerSubtitle=header.findViewById(R.id.subtitle);
|
||||||
|
openLinkButton=header.findViewById(R.id.profile_action_btn);
|
||||||
|
|
||||||
|
headerTitle.setText(trendingLink.title);
|
||||||
|
openLinkButton.setVisibility(View.GONE);
|
||||||
|
openLinkButton.setOnClickListener(v->{
|
||||||
|
if(trendingLink==null)
|
||||||
|
return;
|
||||||
|
openLink();
|
||||||
|
});
|
||||||
|
updateHeader();
|
||||||
|
|
||||||
|
MergeRecyclerAdapter mergeAdapter=new MergeRecyclerAdapter();
|
||||||
|
if(!(getParentFragment() instanceof HomeTabFragment)){
|
||||||
|
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(header));
|
||||||
|
}
|
||||||
|
mergeAdapter.addAdapter(super.getAdapter());
|
||||||
|
return mergeAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getMainAdapterOffset(){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createOptionsMenu(){
|
||||||
|
optionsMenu.clear();
|
||||||
|
optionsMenuInflater.inflate(R.menu.trending_links_timeline, optionsMenu);
|
||||||
|
MenuItem openLinkMenuItem=optionsMenu.findItem(R.id.open_link);
|
||||||
|
openLinkMenuItem.setVisible(toolbarContentVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||||
|
inflater.inflate(R.menu.trending_links_timeline, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
optionsMenu=menu;
|
||||||
|
optionsMenuInflater=inflater;
|
||||||
|
createOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
|
if (super.onOptionsItemSelected(item)) return true;
|
||||||
|
if (item.getItemId() == R.id.open_link && trendingLink!=null) {
|
||||||
|
openLink();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
super.onUpdateToolbar();
|
||||||
|
toolbarTitleView.setAlpha(toolbarContentVisible ? 1f : 0f);
|
||||||
|
createOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHeader(){
|
||||||
|
if(trendingLink==null || getActivity()==null)
|
||||||
|
return;
|
||||||
|
//TODO: update to show mastodon account once fully implemented upstream
|
||||||
|
headerSubtitle.setText(getContext().getString(R.string.article_by_author, TextUtils.isEmpty(trendingLink.authorName)? trendingLink.providerName : trendingLink.authorName));
|
||||||
|
openLinkButton.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openLink() {
|
||||||
|
UiUtils.launchWebBrowser(getActivity(), trendingLink.url);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M17.75,2.007a2.25,2.25 0,0 1,2.245 2.096l0.005,0.154v15.498A2.25,2.25 0,0 1,17.904 22l-0.154,0.005H6.25a2.25,2.25 0,0 1,-2.245 -2.096L4,19.755V4.257a2.25,2.25 0,0 1,2.096 -2.245l0.154,-0.005h11.5ZM7.75,7a0.75,0.75 0,1 0,0 1.5h8.5a0.75,0.75 0,0 0,0 -1.5h-8.5ZM7,11.75c0,0.414 0.336,0.75 0.75,0.75h8.5a0.75,0.75 0,0 0,0 -1.5h-8.5a0.75,0.75 0,0 0,-0.75 0.75ZM7.75,15a0.75,0.75 0,1 0,0 1.5h8.5a0.75,0.75 0,0 0,0 -1.5h-8.5Z"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toStartOf="@id/button_wrap"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:textAppearance="@style/m3_headline_small"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
tools:text="Microsoft Chose Profit Over Security"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/button_wrap"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignTop="@id/title"
|
||||||
|
android:layout_alignBottom="@id/title"
|
||||||
|
android:layout_alignParentEnd="true">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/profile_action_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:text="@string/mo_trending_link_read"
|
||||||
|
tools:text="@string/mark_all_notifications_read" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/title"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textAppearance="@style/m3_label_large"
|
||||||
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
|
tools:text="@string/article_by_author"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/open_link"
|
||||||
|
android:icon="@drawable/ic_fluent_document_one_page_24_filled"
|
||||||
|
android:showAsAction="always"
|
||||||
|
android:title="@string/mo_trending_link_read"/>
|
||||||
|
</menu>
|
|
@ -138,4 +138,5 @@
|
||||||
<string name="mo_error_display_title">Failed to display post</string>
|
<string name="mo_error_display_title">Failed to display post</string>
|
||||||
<string name="mo_error_display_text">Something went wrong while loading this post. If the problem persists, please report it on our Issues page along with the error details.</string>
|
<string name="mo_error_display_text">Something went wrong while loading this post. If the problem persists, please report it on our Issues page along with the error details.</string>
|
||||||
<string name="mo_error_display_copy_error_details">Copy details</string>
|
<string name="mo_error_display_copy_error_details">Copy details</string>
|
||||||
|
<string name="mo_trending_link_read">Read</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue