GitNex/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java
qwerty287 7e514041bb Don't use TinyDB as cache (#1034)
Do not use TinyDB as a cache or a way to send data between activities.

### How is this working

Instead of saving everything into the TinyDB, I created three `Context`s (a `RepositoryContext`, an `IssueContext` and an `AccountContext`). All are used to store things like API or database values/models and additional data, e.g. the `RepositoryContext` also contains information about the current filter state of a repository (issues, pull requests, releases/tags and milestones). These are sent using `Intent`s and `Bundle`s between activities and fragments. Changing a field (e.g. filter state) in any fragment changes it also for the whole repository (or at least it should do so).
Due to the size of the changes (after c9172f85ef, Git says `154 files changed, 3318 insertions(+), 3835 deletions(-)`) **I highly recommend you to create a beta/pre release before releasing a stable version**.
Additional changes:
* after logging out, the account remains in the account list (with a note) and you can log in again (you can't switch to this account)
* repositories and organizations are clickable on user profiles
* deleted two unused classes

Once finished, hopefully
* closes #354
* replaces #897
* fixes #947
* closes #1001
* closes #1015
* marks #876 and #578 as `Wontfix` since they are not necessary at this point
* and all the other TinyDB issues

Co-authored-by: qwerty287 <ndev@web.de>
Co-authored-by: M M Arif <mmarif@noreply.codeberg.org>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1034
Reviewed-by: 6543 <6543@noreply.codeberg.org>
Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org>
Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
2022-03-13 03:59:13 +01:00

287 lines
11 KiB
Java

package org.mian.gitnex.fragments;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.gitnex.tea4j.models.Issues;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.BaseActivity;
import org.mian.gitnex.activities.RepoDetailActivity;
import org.mian.gitnex.adapters.IssuesAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.FragmentIssuesBinding;
import org.mian.gitnex.helpers.Constants;
import org.mian.gitnex.helpers.SnackBar;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Author M M Arif
*/
public class IssuesFragment extends Fragment {
public static boolean resumeIssues = false;
private FragmentIssuesBinding fragmentIssuesBinding;
private Context context;
private Menu menu;
private List<Issues> issuesList;
private IssuesAdapter adapter;
private int pageSize = Constants.issuesPageInit;
private final String TAG = Constants.tagIssuesList;
private int resultLimit = Constants.resultLimitOldGiteaInstances;
private final String requestType = Constants.issuesRequestType;
private RepositoryContext repository;
public static IssuesFragment newInstance(RepositoryContext repository) {
IssuesFragment f = new IssuesFragment();
f.setArguments(repository.getBundle());
return f;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
fragmentIssuesBinding = FragmentIssuesBinding.inflate(inflater, container, false);
setHasOptionsMenu(true);
context = getContext();
repository = RepositoryContext.fromBundle(requireArguments());
// if gitea is 1.12 or higher use the new limit
if(((BaseActivity) requireActivity()).getAccount().requiresVersion("1.12.0")) {
resultLimit = Constants.resultLimitNewGiteaInstances;
}
issuesList = new ArrayList<>();
fragmentIssuesBinding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> {
fragmentIssuesBinding.pullToRefresh.setRefreshing(false);
loadInitial(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), resultLimit, requestType, repository.getIssueState().toString(), "");
adapter.notifyDataChanged();
}, 200));
adapter = new IssuesAdapter(context, issuesList);
adapter.setLoadMoreListener(() -> fragmentIssuesBinding.recyclerView.post(() -> {
if(issuesList.size() == resultLimit || pageSize == resultLimit) {
int page = (issuesList.size() + resultLimit) / resultLimit;
loadMore(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), page, resultLimit, requestType, repository.getIssueState().toString(), "");
}
}));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(fragmentIssuesBinding.recyclerView.getContext(), DividerItemDecoration.VERTICAL);
fragmentIssuesBinding.recyclerView.setHasFixedSize(true);
fragmentIssuesBinding.recyclerView.addItemDecoration(dividerItemDecoration);
fragmentIssuesBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context));
fragmentIssuesBinding.recyclerView.setAdapter(adapter);
((RepoDetailActivity) requireActivity()).setFragmentRefreshListener(issueState -> {
if(issueState.equals("closed")) {
menu.getItem(1).setIcon(R.drawable.ic_filter_closed);
}
else {
menu.getItem(1).setIcon(R.drawable.ic_filter);
}
issuesList.clear();
adapter = new IssuesAdapter(context, issuesList);
adapter.setLoadMoreListener(() -> fragmentIssuesBinding.recyclerView.post(() -> {
if(issuesList.size() == resultLimit || pageSize == resultLimit) {
int page = (issuesList.size() + resultLimit) / resultLimit;
loadMore(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), page, resultLimit, requestType, repository.getIssueState().toString(), repository.getIssueMilestoneFilterName());
}
}));
fragmentIssuesBinding.progressBar.setVisibility(View.VISIBLE);
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE);
loadInitial(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), resultLimit, requestType, issueState, repository.getIssueMilestoneFilterName());
fragmentIssuesBinding.recyclerView.setAdapter(adapter);
});
((RepoDetailActivity) requireActivity()).setFragmentRefreshListenerFilterIssuesByMilestone(filterIssueByMilestone -> {
issuesList.clear();
adapter = new IssuesAdapter(context, issuesList);
adapter.setLoadMoreListener(() -> fragmentIssuesBinding.recyclerView.post(() -> {
if(issuesList.size() == resultLimit || pageSize == resultLimit) {
int page = (issuesList.size() + resultLimit) / resultLimit;
loadMore(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), page, resultLimit, requestType, repository.getIssueState().toString(), repository.getIssueMilestoneFilterName());
}
}));
fragmentIssuesBinding.progressBar.setVisibility(View.VISIBLE);
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE);
loadInitial(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), resultLimit, requestType, repository.getIssueState().toString(), filterIssueByMilestone);
fragmentIssuesBinding.recyclerView.setAdapter(adapter);
});
loadInitial(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), resultLimit, requestType, repository.getIssueState().toString(), repository.getIssueMilestoneFilterName());
return fragmentIssuesBinding.getRoot();
}
@Override
public void onResume() {
super.onResume();
if(resumeIssues) {
loadInitial(((BaseActivity) requireActivity()).getAccount().getAuthorization(), repository.getOwner(), repository.getName(), resultLimit, requestType, repository.getIssueState().toString(), repository.getIssueMilestoneFilterName());
resumeIssues = false;
}
}
private void loadInitial(String token, String repoOwner, String repoName, int resultLimit, String requestType, String issueState, String filterByMilestone) {
Call<List<Issues>> call = RetrofitClient.getApiInterface(context).getIssues(token, repoOwner, repoName, 1, resultLimit, requestType, issueState, filterByMilestone);
call.enqueue(new Callback<List<Issues>>() {
@Override
public void onResponse(@NonNull Call<List<Issues>> call, @NonNull Response<List<Issues>> response) {
if(response.code() == 200) {
assert response.body() != null;
if(response.body().size() > 0) {
issuesList.clear();
issuesList.addAll(response.body());
adapter.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE);
}
else {
issuesList.clear();
adapter.notifyDataChanged();
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE);
}
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
}
else if(response.code() == 404) {
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE);
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
}
else {
Log.e(TAG, String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<List<Issues>> call, @NonNull Throwable t) {
Log.e(TAG, t.toString());
}
});
}
private void loadMore(String token, String repoOwner, String repoName, int page, int resultLimit, String requestType, String issueState, String filterByMilestone) {
fragmentIssuesBinding.progressBar.setVisibility(View.VISIBLE);
Call<List<Issues>> call = RetrofitClient.getApiInterface(context).getIssues(token, repoOwner, repoName, page, resultLimit, requestType, issueState, filterByMilestone);
call.enqueue(new Callback<List<Issues>>() {
@Override
public void onResponse(@NonNull Call<List<Issues>> call, @NonNull Response<List<Issues>> response) {
if(response.code() == 200) {
List<Issues> result = response.body();
assert result != null;
if(result.size() > 0) {
pageSize = result.size();
issuesList.addAll(result);
}
else {
SnackBar.info(context, fragmentIssuesBinding.getRoot(), getString(R.string.noMoreData));
adapter.setMoreDataAvailable(false);
}
adapter.notifyDataChanged();
fragmentIssuesBinding.progressBar.setVisibility(View.GONE);
}
else {
Log.e(TAG, String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<List<Issues>> call, @NonNull Throwable t) {
Log.e(TAG, t.toString());
}
});
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
this.menu = menu;
inflater.inflate(R.menu.search_menu, menu);
inflater.inflate(R.menu.filter_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
if(repository.getIssueState().toString().equals("closed")) {
menu.getItem(1).setIcon(R.drawable.ic_filter_closed);
}
else {
menu.getItem(1).setIcon(R.drawable.ic_filter);
}
MenuItem searchItem = menu.findItem(R.id.action_search);
androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
filter(newText);
return false;
}
});
}
private void filter(String text) {
List<Issues> arr = new ArrayList<>();
for(Issues d : issuesList) {
if(d == null || d.getTitle() == null || d.getBody() == null) {
continue;
}
if(d.getTitle().toLowerCase().contains(text) || d.getBody().toLowerCase().contains(text) || String.valueOf(d.getNumber()).startsWith(text)) {
arr.add(d);
}
}
adapter.updateList(arr);
}
}