mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2025-07-10 13:40:28 +03:00
Follow Twitter/X tags through Nitter
This commit is contained in:
parent
0d2ae2eedf
commit
88c18cc487
8 changed files with 54 additions and 17 deletions
|
@ -212,6 +212,10 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
|
|||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
|
||||
String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
url = "https://" + nitterHost + "/" + instanceName.replaceAll("[ ]+", ",").replaceAll("\\s", "") + "/with_replies/rss";
|
||||
}else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_tags) {
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
|
||||
String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
url = "https://" + nitterHost + "/search?f=tweets&q=" + instanceName.replaceAll("[ ]+", "+or+").replaceAll("\\s", "") + "&e-nativeretweets=on";
|
||||
}
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
|
@ -257,6 +261,8 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
|
|||
instanceType = RemoteInstance.InstanceType.GNU;
|
||||
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) {
|
||||
instanceType = RemoteInstance.InstanceType.NITTER;
|
||||
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_tags) {
|
||||
instanceType = RemoteInstance.InstanceType.NITTER_TAG;
|
||||
}
|
||||
RemoteInstance remoteInstance = new RemoteInstance();
|
||||
remoteInstance.type = instanceType;
|
||||
|
|
|
@ -46,6 +46,8 @@ public class RemoteInstance implements Serializable {
|
|||
PEERTUBE("PEERTUBE"),
|
||||
@SerializedName("NITTER")
|
||||
NITTER("NITTER"),
|
||||
@SerializedName("NITTER_TAG")
|
||||
NITTER_TAG("NITTER_TAG"),
|
||||
@SerializedName("MISSKEY")
|
||||
MISSKEY("MISSKEY"),
|
||||
@SerializedName("LEMMY")
|
||||
|
|
|
@ -341,10 +341,10 @@ public class PinnedTimelineHelper {
|
|||
break;
|
||||
case REMOTE:
|
||||
name = pinnedTimeline.remoteInstance.host;
|
||||
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
String remoteInstance = sharedpreferences.getString(activity.getString(R.string.SET_NITTER_HOST), activity.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
//Custom name for Nitter instances
|
||||
if (pinnedTimeline.remoteInstance.displayName != null && pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
|
||||
if (pinnedTimeline.remoteInstance.displayName != null && !pinnedTimeline.remoteInstance.displayName.trim().isEmpty()) {
|
||||
name = pinnedTimeline.remoteInstance.displayName;
|
||||
}
|
||||
ident = "@R@" + remoteInstance;
|
||||
|
@ -378,6 +378,7 @@ public class PinnedTimelineHelper {
|
|||
case MISSKEY:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.misskey);
|
||||
break;
|
||||
case NITTER_TAG:
|
||||
case NITTER:
|
||||
tabCustomViewBinding.icon.setImageResource(R.drawable.nitter);
|
||||
break;
|
||||
|
@ -471,9 +472,10 @@ public class PinnedTimelineHelper {
|
|||
case PIXELFED:
|
||||
item.setIcon(R.drawable.pixelfed);
|
||||
break;
|
||||
case NITTER_TAG:
|
||||
case NITTER:
|
||||
item.setIcon(R.drawable.nitter);
|
||||
if (pinnedTimeline.remoteInstance.displayName != null && pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
|
||||
if (pinnedTimeline.remoteInstance.displayName != null && !pinnedTimeline.remoteInstance.displayName.trim().isEmpty()) {
|
||||
item.setTitle(pinnedTimeline.remoteInstance.displayName);
|
||||
} else {
|
||||
item.setTitle(pinnedTimeline.remoteInstance.host);
|
||||
|
@ -525,7 +527,7 @@ public class PinnedTimelineHelper {
|
|||
bubbleClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
|
||||
break;
|
||||
case REMOTE:
|
||||
if (pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER && pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
instanceClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
|
||||
} else {
|
||||
nitterClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
|
||||
|
@ -1528,6 +1530,12 @@ public class PinnedTimelineHelper {
|
|||
PopupMenu popup = new PopupMenu(activity, view);
|
||||
popup.getMenuInflater()
|
||||
.inflate(R.menu.option_nitter_timeline, popup.getMenu());
|
||||
if(remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
MenuItem item = popup.getMenu().findItem(R.id.action_nitter_manage_accounts);
|
||||
if(item != null) {
|
||||
item.setTitle(R.string.manage_tags);
|
||||
}
|
||||
}
|
||||
int finalOffSetPosition = offSetPosition;
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
int itemId = item.getItemId();
|
||||
|
@ -1547,7 +1555,7 @@ public class PinnedTimelineHelper {
|
|||
}
|
||||
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
|
||||
String values = editTextName.getText().toString();
|
||||
if (values.trim().length() == 0) {
|
||||
if (values.trim().isEmpty()) {
|
||||
values = remoteInstance.displayName;
|
||||
}
|
||||
remoteInstance.displayName = values;
|
||||
|
|
|
@ -100,14 +100,15 @@ public class ReorderTabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||
case GNU:
|
||||
holder.binding.icon.setImageResource(R.drawable.ic_gnu_social);
|
||||
break;
|
||||
case NITTER_TAG:
|
||||
case NITTER:
|
||||
holder.binding.icon.setImageResource(R.drawable.nitter);
|
||||
break;
|
||||
}
|
||||
if (pinned.pinnedTimelines.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinned.pinnedTimelines.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER && pinned.pinnedTimelines.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
holder.binding.text.setText(pinned.pinnedTimelines.get(position).remoteInstance.host);
|
||||
} else {
|
||||
if (pinned.pinnedTimelines.get(position).remoteInstance.displayName != null && pinned.pinnedTimelines.get(position).remoteInstance.displayName.trim().length() > 0) {
|
||||
if (pinned.pinnedTimelines.get(position).remoteInstance.displayName != null && !pinned.pinnedTimelines.get(position).remoteInstance.displayName.trim().isEmpty()) {
|
||||
holder.binding.text.setText(pinned.pinnedTimelines.get(position).remoteInstance.displayName);
|
||||
} else {
|
||||
holder.binding.text.setText(pinned.pinnedTimelines.get(position).remoteInstance.host);
|
||||
|
|
|
@ -420,7 +420,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
pinnedTimeline = (PinnedTimeline) bundle.getSerializable(Helper.ARG_REMOTE_INSTANCE);
|
||||
canBeFederated = true;
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null) {
|
||||
if (pinnedTimeline.remoteInstance.type != RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinnedTimeline.remoteInstance.type != RemoteInstance.InstanceType.NITTER && pinnedTimeline.remoteInstance.type != RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
remoteInstance = pinnedTimeline.remoteInstance.host;
|
||||
} else {
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||
|
@ -471,7 +471,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
} else if (list_id != null) {
|
||||
ident = "@l@" + list_id;
|
||||
} else if (remoteInstance != null && !checkRemotely) {
|
||||
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG) {
|
||||
ident = "@R@" + pinnedTimeline.remoteInstance.host;
|
||||
} else {
|
||||
ident = "@R@" + remoteInstance;
|
||||
|
@ -562,7 +562,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
//Update the timeline with new statuses
|
||||
int insertedStatus;
|
||||
if(pinnedTimeline!= null && pinnedTimeline.remoteInstance != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||
if(pinnedTimeline!= null && pinnedTimeline.remoteInstance != null && (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG)) {
|
||||
insertedStatus = fetched_statuses.statuses.size();
|
||||
int fromPosition = timelineStatuses.size();
|
||||
timelineStatuses.addAll(fetched_statuses.statuses);
|
||||
|
@ -685,7 +685,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
max_id = statuses.pagination.max_id;
|
||||
}
|
||||
//For Lemmy and Nitter pagination
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null && (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER)) {
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null && (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG)) {
|
||||
max_id = statuses.pagination.max_id;
|
||||
}
|
||||
if (min_id == null || (statuses.pagination.min_id != null && Helper.compareTo(statuses.pagination.min_id, min_id) > 0)) {
|
||||
|
@ -1049,20 +1049,20 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
routeCommon(direction, fetchingMissing, fetchStatus);
|
||||
} else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE
|
||||
//NITTER TIMELINES
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||
if (pinnedTimeline != null && (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG)) {
|
||||
if (direction == null) {
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.host, null)
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.type, pinnedTimeline.remoteInstance.host, null)
|
||||
.observe(getViewLifecycleOwner(), nitterStatuses -> {
|
||||
initialStatuses = nitterStatuses;
|
||||
initializeStatusesCommonView(nitterStatuses);
|
||||
});
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.host, max_id)
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.type, pinnedTimeline.remoteInstance.host, max_id)
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
flagLoading = false;
|
||||
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.host, null)
|
||||
timelinesVM.getNitterHTML(pinnedTimeline.remoteInstance.type, pinnedTimeline.remoteInstance.host, null)
|
||||
.observe(getViewLifecycleOwner(), statusesRefresh -> {
|
||||
if (statusAdapter != null) {
|
||||
dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
|
||||
|
|
|
@ -56,6 +56,7 @@ import app.fedilab.android.mastodon.client.entities.api.Status;
|
|||
import app.fedilab.android.mastodon.client.entities.api.Statuses;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Tag;
|
||||
import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
|
||||
import app.fedilab.android.mastodon.client.entities.app.RemoteInstance;
|
||||
import app.fedilab.android.mastodon.client.entities.app.StatusCache;
|
||||
import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
|
||||
import app.fedilab.android.mastodon.client.entities.app.Timeline;
|
||||
|
@ -300,6 +301,7 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
* @return {@link LiveData} containing a {@link Statuses}
|
||||
*/
|
||||
public LiveData<Statuses> getNitterHTML(
|
||||
RemoteInstance.InstanceType instanceType,
|
||||
String accountsStr,
|
||||
String max_position) {
|
||||
statusesMutableLiveData = new MutableLiveData<>();
|
||||
|
@ -307,9 +309,20 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String nitterInstance = sharedpreferences.getString(context.getString(R.string.SET_NITTER_HOST), context.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
final String fedilabInstance = "nitter.fedilab.app";
|
||||
accountsStr = accountsStr.replaceAll("\\s", ",").replaceAll(",,",",");
|
||||
|
||||
String cursor = max_position == null ? "" : max_position;
|
||||
String url = "https://"+fedilabInstance+"/" + accountsStr + "/with_replies" +cursor;
|
||||
String url;
|
||||
accountsStr = accountsStr.replaceAll("\\s", ",").replaceAll(",,",",");
|
||||
if(instanceType == RemoteInstance.InstanceType.NITTER) {
|
||||
url = "https://"+fedilabInstance+"/" + accountsStr + "/with_replies" +cursor;
|
||||
} else {
|
||||
String[] tags = accountsStr.split(",");
|
||||
StringBuilder tagsQuery = new StringBuilder();
|
||||
for(String tag: tags) {
|
||||
tagsQuery.append("%23").append(tag).append("+or+");
|
||||
}
|
||||
url = "https://"+fedilabInstance+"/search?f=tweets&q=" + tagsQuery +"&e-nativeretweets=on&" +cursor;
|
||||
}
|
||||
Request request = new Request.Builder()
|
||||
.header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE)
|
||||
.url(url)
|
||||
|
|
|
@ -65,6 +65,12 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/twitter_accounts" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/twitter_tags"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/twitter_tags" />
|
||||
</RadioGroup>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -618,6 +618,7 @@
|
|||
<string name="set_utm_parameters_indication">The app will automatically remove UTM parameters from URLs before visiting a link.</string>
|
||||
<string name="talking_about">%d people talking</string>
|
||||
<string name="twitter_accounts">Twitter accounts (via Nitter)</string>
|
||||
<string name="twitter_tags">Twitter tags (via Nitter)</string>
|
||||
<string name="list_of_twitter_accounts">Twitter usernames space separated</string>
|
||||
<string name="identity_proofs">Identity proofs</string>
|
||||
<string name="verified_user">Verified identity</string>
|
||||
|
|
Loading…
Reference in a new issue