Refreshing uses since_id wherever applicable. Also, reverted the notification icon.

This commit is contained in:
Vavassor 2017-03-10 15:12:40 -05:00
parent eed8168e8a
commit 851a92a271
17 changed files with 43 additions and 134 deletions

View File

@ -25,15 +25,12 @@ import java.util.List;
abstract class AccountAdapter extends RecyclerView.Adapter { abstract class AccountAdapter extends RecyclerView.Adapter {
List<Account> accountList; List<Account> accountList;
AccountActionListener accountActionListener; AccountActionListener accountActionListener;
FooterActionListener footerActionListener;
FooterViewHolder.State footerState; FooterViewHolder.State footerState;
AccountAdapter(AccountActionListener accountActionListener, AccountAdapter(AccountActionListener accountActionListener) {
FooterActionListener footerActionListener) {
super(); super();
accountList = new ArrayList<>(); accountList = new ArrayList<>();
this.accountActionListener = accountActionListener; this.accountActionListener = accountActionListener;
this.footerActionListener = footerActionListener;
footerState = FooterViewHolder.State.LOADING; footerState = FooterViewHolder.State.LOADING;
} }

View File

@ -37,8 +37,7 @@ import java.util.List;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
public class AccountFragment extends Fragment implements AccountActionListener, public class AccountFragment extends Fragment implements AccountActionListener {
FooterActionListener {
private static final String TAG = "Account"; // logging tag private static final String TAG = "Account"; // logging tag
public enum Type { public enum Type {
@ -106,7 +105,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
AccountAdapter adapter = (AccountAdapter) view.getAdapter(); AccountAdapter adapter = (AccountAdapter) view.getAdapter();
Account account = adapter.getItem(adapter.getItemCount() - 2); Account account = adapter.getItem(adapter.getItemCount() - 2);
if (account != null) { if (account != null) {
fetchAccounts(account.id); fetchAccounts(account.id, null);
} else { } else {
fetchAccounts(); fetchAccounts();
} }
@ -114,9 +113,9 @@ public class AccountFragment extends Fragment implements AccountActionListener,
}; };
recyclerView.addOnScrollListener(scrollListener); recyclerView.addOnScrollListener(scrollListener);
if (type == Type.BLOCKS) { if (type == Type.BLOCKS) {
adapter = new BlocksAdapter(this, this); adapter = new BlocksAdapter(this);
} else { } else {
adapter = new FollowAdapter(this, this); adapter = new FollowAdapter(this);
} }
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
@ -151,7 +150,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
super.onDestroyView(); super.onDestroyView();
} }
private void fetchAccounts(final String fromId) { private void fetchAccounts(final String fromId, String uptoId) {
Callback<List<Account>> cb = new Callback<List<Account>>() { Callback<List<Account>> cb = new Callback<List<Account>>() {
@Override @Override
public void onResponse(Call<List<Account>> call, retrofit2.Response<List<Account>> response) { public void onResponse(Call<List<Account>> call, retrofit2.Response<List<Account>> response) {
@ -167,22 +166,22 @@ public class AccountFragment extends Fragment implements AccountActionListener,
switch (type) { switch (type) {
default: default:
case FOLLOWS: { case FOLLOWS: {
api.accountFollowing(accountId, fromId, null, null).enqueue(cb); api.accountFollowing(accountId, fromId, uptoId, null).enqueue(cb);
break; break;
} }
case FOLLOWERS: { case FOLLOWERS: {
api.accountFollowers(accountId, fromId, null, null).enqueue(cb); api.accountFollowers(accountId, fromId, uptoId, null).enqueue(cb);
break; break;
} }
case BLOCKS: { case BLOCKS: {
api.blocks(fromId, null, null).enqueue(cb); api.blocks(fromId, uptoId, null).enqueue(cb);
break; break;
} }
} }
} }
private void fetchAccounts() { private void fetchAccounts() {
fetchAccounts(null); fetchAccounts(null, null);
} }
private static boolean findAccount(List<Account> accounts, String id) { private static boolean findAccount(List<Account> accounts, String id) {
@ -229,15 +228,6 @@ public class AccountFragment extends Fragment implements AccountActionListener,
} }
} }
public void onLoadMore() {
Account account = adapter.getItem(adapter.getItemCount() - 2);
if (account != null) {
fetchAccounts(account.id);
} else {
fetchAccounts();
}
}
public void onViewAccount(String id) { public void onViewAccount(String id) {
Intent intent = new Intent(getContext(), AccountActivity.class); Intent intent = new Intent(getContext(), AccountActivity.class);
intent.putExtra("id", id); intent.putExtra("id", id);

View File

@ -35,9 +35,8 @@ class BlocksAdapter extends AccountAdapter {
private Set<Integer> unblockedAccountPositions; private Set<Integer> unblockedAccountPositions;
BlocksAdapter(AccountActionListener accountActionListener, BlocksAdapter(AccountActionListener accountActionListener) {
FooterActionListener footerActionListener) { super(accountActionListener);
super(accountActionListener, footerActionListener);
unblockedAccountPositions = new HashSet<>(); unblockedAccountPositions = new HashSet<>();
} }

View File

@ -31,9 +31,8 @@ class FollowAdapter extends AccountAdapter {
private static final int VIEW_TYPE_ACCOUNT = 0; private static final int VIEW_TYPE_ACCOUNT = 0;
private static final int VIEW_TYPE_FOOTER = 1; private static final int VIEW_TYPE_FOOTER = 1;
FollowAdapter(AccountActionListener accountActionListener, FollowAdapter(AccountActionListener accountActionListener) {
FooterActionListener footerActionListener) { super(accountActionListener);
super(accountActionListener, footerActionListener);
} }
@Override @Override

View File

@ -1,20 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is part of Tusky.
*
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky. If not, see
* <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky;
public interface FooterActionListener {
void onLoadMore();
}

View File

@ -82,7 +82,7 @@ public class LoginActivity extends BaseActivity {
private void redirectUserToAuthorizeAndLogin() { private void redirectUserToAuthorizeAndLogin() {
/* To authorize this app and log in it's necessary to redirect to the domain given, /* To authorize this app and log in it's necessary to redirect to the domain given,
* activity_login there, and the server will redirect back to the app with its response. */ * activity_login there, and the server will redirect back to the app with its response. */
String endpoint = getString(R.string.endpoint_authorize); String endpoint = MastodonAPI.ENDPOINT_AUTHORIZE;
String redirectUri = getOauthRedirectUri(); String redirectUri = getOauthRedirectUri();
Map<String, String> parameters = new HashMap<>(); Map<String, String> parameters = new HashMap<>();
parameters.put("client_id", clientId); parameters.put("client_id", clientId);

View File

@ -25,6 +25,8 @@ import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
public interface MastodonAPI { public interface MastodonAPI {
String ENDPOINT_AUTHORIZE = "/oauth/authorize";
@GET("api/v1/timelines/home") @GET("api/v1/timelines/home")
Call<List<Status>> homeTimeline( Call<List<Status>> homeTimeline(
@Query("max_id") String maxId, @Query("max_id") String maxId,

View File

@ -44,16 +44,13 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe
private List<Notification> notifications; private List<Notification> notifications;
private StatusActionListener statusListener; private StatusActionListener statusListener;
private FollowListener followListener; private FollowListener followListener;
private FooterActionListener footerListener;
private FooterViewHolder.State footerState; private FooterViewHolder.State footerState;
NotificationsAdapter(StatusActionListener statusListener, FollowListener followListener, NotificationsAdapter(StatusActionListener statusListener, FollowListener followListener) {
FooterActionListener footerListener) {
super(); super();
notifications = new ArrayList<>(); notifications = new ArrayList<>();
this.statusListener = statusListener; this.statusListener = statusListener;
this.followListener = followListener; this.followListener = followListener;
this.footerListener = footerListener;
footerState = FooterViewHolder.State.LOADING; footerState = FooterViewHolder.State.LOADING;
} }
@ -187,7 +184,6 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe
interface FollowListener { interface FollowListener {
void onViewAccount(String id); void onViewAccount(String id);
void onFollow(String id);
} }
private static class FollowViewHolder extends RecyclerView.ViewHolder { private static class FollowViewHolder extends RecyclerView.ViewHolder {

View File

@ -38,7 +38,7 @@ import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
public class NotificationsFragment extends SFragment implements public class NotificationsFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener, SwipeRefreshLayout.OnRefreshListener, StatusActionListener,
NotificationsAdapter.FollowListener { NotificationsAdapter.FollowListener {
private static final String TAG = "Notifications"; // logging tag private static final String TAG = "Notifications"; // logging tag
@ -91,14 +91,14 @@ public class NotificationsFragment extends SFragment implements
NotificationsAdapter adapter = (NotificationsAdapter) view.getAdapter(); NotificationsAdapter adapter = (NotificationsAdapter) view.getAdapter();
Notification notification = adapter.getItem(adapter.getItemCount() - 2); Notification notification = adapter.getItem(adapter.getItemCount() - 2);
if (notification != null) { if (notification != null) {
sendFetchNotificationsRequest(notification.id); sendFetchNotificationsRequest(notification.id, null);
} else { } else {
sendFetchNotificationsRequest(); sendFetchNotificationsRequest();
} }
} }
}; };
recyclerView.addOnScrollListener(scrollListener); recyclerView.addOnScrollListener(scrollListener);
adapter = new NotificationsAdapter(this, this, this); adapter = new NotificationsAdapter(this, this);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
TabLayout layout = (TabLayout) getActivity().findViewById(R.id.tab_layout); TabLayout layout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
@ -116,8 +116,6 @@ public class NotificationsFragment extends SFragment implements
}; };
layout.addOnTabSelectedListener(onTabSelectedListener); layout.addOnTabSelectedListener(onTabSelectedListener);
sendFetchNotificationsRequest();
return rootView; return rootView;
} }
@ -133,10 +131,10 @@ public class NotificationsFragment extends SFragment implements
scrollListener.reset(); scrollListener.reset();
} }
private void sendFetchNotificationsRequest(final String fromId) { private void sendFetchNotificationsRequest(final String fromId, String uptoId) {
MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI;
api.notifications(fromId, null, null).enqueue(new Callback<List<Notification>>() { api.notifications(fromId, uptoId, null).enqueue(new Callback<List<Notification>>() {
@Override @Override
public void onResponse(Call<List<Notification>> call, retrofit2.Response<List<Notification>> response) { public void onResponse(Call<List<Notification>> call, retrofit2.Response<List<Notification>> response) {
onFetchNotificationsSuccess(response.body(), fromId); onFetchNotificationsSuccess(response.body(), fromId);
@ -150,7 +148,7 @@ public class NotificationsFragment extends SFragment implements
} }
private void sendFetchNotificationsRequest() { private void sendFetchNotificationsRequest() {
sendFetchNotificationsRequest(null); sendFetchNotificationsRequest(null, null);
} }
private static boolean findNotification(List<Notification> notifications, String id) { private static boolean findNotification(List<Notification> notifications, String id) {
@ -193,13 +191,9 @@ public class NotificationsFragment extends SFragment implements
} }
public void onRefresh() { public void onRefresh() {
sendFetchNotificationsRequest(); Notification notification = adapter.getItem(0);
}
public void onLoadMore() {
Notification notification = adapter.getItem(adapter.getItemCount() - 2);
if (notification != null) { if (notification != null) {
sendFetchNotificationsRequest(notification.id); sendFetchNotificationsRequest(null, notification.id);
} else { } else {
sendFetchNotificationsRequest(); sendFetchNotificationsRequest();
} }
@ -241,8 +235,4 @@ public class NotificationsFragment extends SFragment implements
public void onViewAccount(String id) { public void onViewAccount(String id) {
super.viewAccount(id); super.viewAccount(id);
} }
public void onFollow(String id) {
super.follow(id);
}
} }

View File

@ -20,7 +20,6 @@ import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
public class PreferencesActivity extends BaseActivity public class PreferencesActivity extends BaseActivity
implements SharedPreferences.OnSharedPreferenceChangeListener { implements SharedPreferences.OnSharedPreferenceChangeListener {

View File

@ -32,15 +32,12 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover
private List<Status> statuses; private List<Status> statuses;
private StatusActionListener statusListener; private StatusActionListener statusListener;
private FooterActionListener footerListener;
private FooterViewHolder.State footerState; private FooterViewHolder.State footerState;
TimelineAdapter(StatusActionListener statusListener, TimelineAdapter(StatusActionListener statusListener) {
FooterActionListener footerListener) {
super(); super();
statuses = new ArrayList<>(); statuses = new ArrayList<>();
this.statusListener = statusListener; this.statusListener = statusListener;
this.footerListener = footerListener;
footerState = FooterViewHolder.State.LOADING; footerState = FooterViewHolder.State.LOADING;
} }

View File

@ -18,6 +18,7 @@ package com.keylesspalace.tusky;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.DividerItemDecoration;
@ -35,12 +36,11 @@ import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
public class TimelineFragment extends SFragment implements public class TimelineFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener, StatusActionListener, FooterActionListener { SwipeRefreshLayout.OnRefreshListener, StatusActionListener {
private static final String TAG = "Timeline"; // logging tag private static final String TAG = "Timeline"; // logging tag
public enum Kind { enum Kind {
HOME, HOME,
MENTIONS,
PUBLIC, PUBLIC,
TAG, TAG,
USER, USER,
@ -106,14 +106,14 @@ public class TimelineFragment extends SFragment implements
TimelineAdapter adapter = (TimelineAdapter) view.getAdapter(); TimelineAdapter adapter = (TimelineAdapter) view.getAdapter();
Status status = adapter.getItem(adapter.getItemCount() - 2); Status status = adapter.getItem(adapter.getItemCount() - 2);
if (status != null) { if (status != null) {
sendFetchTimelineRequest(status.id); sendFetchTimelineRequest(status.id, null);
} else { } else {
sendFetchTimelineRequest(); sendFetchTimelineRequest();
} }
} }
}; };
recyclerView.addOnScrollListener(scrollListener); recyclerView.addOnScrollListener(scrollListener);
adapter = new TimelineAdapter(this, this); adapter = new TimelineAdapter(this);
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
if (jumpToTopAllowed()) { if (jumpToTopAllowed()) {
@ -156,7 +156,7 @@ public class TimelineFragment extends SFragment implements
scrollListener.reset(); scrollListener.reset();
} }
private void sendFetchTimelineRequest(final String fromId) { private void sendFetchTimelineRequest(@Nullable final String fromId, @Nullable String uptoId) {
MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI; MastodonAPI api = ((BaseActivity) getActivity()).mastodonAPI;
Callback<List<Status>> cb = new Callback<List<Status>>() { Callback<List<Status>> cb = new Callback<List<Status>>() {
@ -174,30 +174,30 @@ public class TimelineFragment extends SFragment implements
switch (kind) { switch (kind) {
default: default:
case HOME: { case HOME: {
api.homeTimeline(fromId, null, null).enqueue(cb); api.homeTimeline(fromId, uptoId, null).enqueue(cb);
break; break;
} }
case PUBLIC: { case PUBLIC: {
api.publicTimeline(null, fromId, null, null).enqueue(cb); api.publicTimeline(null, fromId, uptoId, null).enqueue(cb);
break; break;
} }
case TAG: { case TAG: {
api.hashtagTimeline(hashtagOrId, null, fromId, null, null).enqueue(cb); api.hashtagTimeline(hashtagOrId, null, fromId, uptoId, null).enqueue(cb);
break; break;
} }
case USER: { case USER: {
api.accountStatuses(hashtagOrId, fromId, null, null).enqueue(cb); api.accountStatuses(hashtagOrId, fromId, uptoId, null).enqueue(cb);
break; break;
} }
case FAVOURITES: { case FAVOURITES: {
api.favourites(fromId, null, null).enqueue(cb); api.favourites(fromId, uptoId, null).enqueue(cb);
break; break;
} }
} }
} }
private void sendFetchTimelineRequest() { private void sendFetchTimelineRequest() {
sendFetchTimelineRequest(null); sendFetchTimelineRequest(null, null);
} }
private static boolean findStatus(List<Status> statuses, String id) { private static boolean findStatus(List<Status> statuses, String id) {
@ -240,13 +240,9 @@ public class TimelineFragment extends SFragment implements
} }
public void onRefresh() { public void onRefresh() {
sendFetchTimelineRequest(); Status status = adapter.getItem(0);
}
public void onLoadMore() {
Status status = adapter.getItem(adapter.getItemCount() - 2);
if (status != null) { if (status != null) {
sendFetchTimelineRequest(status.id); sendFetchTimelineRequest(null, status.id);
} else { } else {
sendFetchTimelineRequest(); sendFetchTimelineRequest();
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -6,42 +6,6 @@
<string name="oauth_redirect_host">oauth2redirect</string> <string name="oauth_redirect_host">oauth2redirect</string>
<string name="preferences_file_key">com.keylesspalace.tusky.PREFERENCES</string> <string name="preferences_file_key">com.keylesspalace.tusky.PREFERENCES</string>
<string name="endpoint_status">/api/v1/statuses</string>
<string name="endpoint_media">/api/v1/media</string>
<string name="endpoint_timelines_home">/api/v1/timelines/home</string>
<string name="endpoint_timelines_mentions">/api/v1/timelines/mentions</string>
<string name="endpoint_timelines_public">/api/v1/timelines/public</string>
<string name="endpoint_timelines_tag">/api/v1/timelines/tag/%s</string>
<string name="endpoint_notifications">/api/v1/notifications</string>
<string name="endpoint_follows">/api/v1/follows</string>
<string name="endpoint_get_status">/api/v1/statuses/%s</string>
<string name="endpoint_accounts">/api/v1/accounts/%s</string>
<string name="endpoint_verify_credentials">/api/v1/accounts/verify_credentials</string>
<string name="endpoint_statuses">/api/v1/accounts/%s/statuses</string>
<string name="endpoint_following">/api/v1/accounts/%s/following</string>
<string name="endpoint_followers">/api/v1/accounts/%s/followers</string>
<string name="endpoint_relationships">/api/v1/accounts/relationships</string>
<string name="endpoint_blocks">/api/v1/blocks</string>
<string name="endpoint_favourites">/api/v1/favourites</string>
<string name="endpoint_delete">/api/v1/statuses/%s</string>
<string name="endpoint_reblog">/api/v1/statuses/%s/reblog</string>
<string name="endpoint_unreblog">/api/v1/statuses/%s/unreblog</string>
<string name="endpoint_favourite">/api/v1/statuses/%s/favourite</string>
<string name="endpoint_unfavourite">/api/v1/statuses/%s/unfavourite</string>
<string name="endpoint_context">/api/v1/statuses/%s/context</string>
<string name="endpoint_reblogged_by">/api/v1/statuses/%s/reblogged_by</string>
<string name="endpoint_favourited_by">/api/v1/statuses/%s/favourited_by</string>
<string name="endpoint_follow">/api/v1/accounts/%s/follow</string>
<string name="endpoint_unfollow">/api/v1/accounts/%s/unfollow</string>
<string name="endpoint_block">/api/v1/accounts/%s/block</string>
<string name="endpoint_unblock">/api/v1/accounts/%s/unblock</string>
<string name="endpoint_reports">/api/v1/reports</string>
<string name="endpoint_apps">/api/v1/apps</string>
<string name="endpoint_authorize">/oauth/authorize</string>
<string name="endpoint_token">/oauth/token</string>
<string name="endpoint_devices_register">/api/v1/devices/register</string>
<string name="endpoint_devices_unregister">/api/v1/devices/unregister</string>
<string name="error_authorization_unknown">An unidentified authorization error occurred.</string> <string name="error_authorization_unknown">An unidentified authorization error occurred.</string>
<string name="error_fetching_notifications">Notifications could not be fetched.</string> <string name="error_fetching_notifications">Notifications could not be fetched.</string>
<string name="error_compose_character_limit">The status is too long!</string> <string name="error_compose_character_limit">The status is too long!</string>
@ -157,12 +121,12 @@
<string name="action_compose_options">Privacy options</string> <string name="action_compose_options">Privacy options</string>
<string name="login_success">Welcome back!</string> <string name="login_success">Welcome back!</string>
<string name="action_share">Share</string> <string name="action_share">Share</string>
<string name="send_status_to">Share toot URL to...</string> <string name="send_status_to">Share toot URL to</string>
<string name="action_mute">Mute</string> <string name="action_mute">Mute</string>
<string name="action_unmute">Unmute</string> <string name="action_unmute">Unmute</string>
<string name="error_unmuting">That user wasn\'t unmuted.</string> <string name="error_unmuting">That user wasn\'t unmuted.</string>
<string name="error_muting">That user wasn\'t muted.</string> <string name="error_muting">That user wasn\'t muted.</string>
<string name="search">Search accounts...</string> <string name="search">Search accounts</string>
<string name="toggle_nsfw">NSFW</string> <string name="toggle_nsfw">NSFW</string>
</resources> </resources>