mirror of
https://github.com/gitnex-org/gitnex.git
synced 2026-06-10 01:13:28 -05:00
Refactor instance administration - unadopted repositories
This commit is contained in:
@@ -39,9 +39,6 @@
|
||||
<activity
|
||||
android:name=".activities.AdminGetUsersActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/>
|
||||
<activity
|
||||
android:name=".activities.AdminUnadoptedReposActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/>
|
||||
<activity
|
||||
android:name=".activities.CreateReleaseActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/>
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package org.mian.gitnex.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.adapters.AdminUnadoptedReposAdapter;
|
||||
import org.mian.gitnex.databinding.ActivityAdminCronTasksBinding;
|
||||
import org.mian.gitnex.helpers.Constants;
|
||||
import org.mian.gitnex.viewmodels.AdminUnadoptedReposViewModel;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
* @author qwerty287
|
||||
*/
|
||||
public class AdminUnadoptedReposActivity extends BaseActivity {
|
||||
|
||||
private AdminUnadoptedReposViewModel viewModel;
|
||||
private View.OnClickListener onClickListener;
|
||||
private AdminUnadoptedReposAdapter adapter;
|
||||
|
||||
private ActivityAdminCronTasksBinding binding;
|
||||
|
||||
private int PAGE = 1;
|
||||
private int resultLimit;
|
||||
private boolean reload = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityAdminCronTasksBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
viewModel = new ViewModelProvider(this).get(AdminUnadoptedReposViewModel.class);
|
||||
|
||||
resultLimit = Constants.getCurrentResultLimit(ctx);
|
||||
initCloseListener();
|
||||
binding.close.setOnClickListener(onClickListener);
|
||||
|
||||
Toolbar toolbar = binding.toolbar;
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
binding.toolbarTitle.setText(R.string.unadoptedRepos);
|
||||
|
||||
binding.recyclerView.setHasFixedSize(true);
|
||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx));
|
||||
|
||||
binding.pullToRefresh.setOnRefreshListener(
|
||||
() ->
|
||||
new Handler(Looper.getMainLooper())
|
||||
.postDelayed(
|
||||
() -> {
|
||||
binding.pullToRefresh.setRefreshing(false);
|
||||
PAGE = 1;
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
reload = true;
|
||||
viewModel.loadRepos(ctx, PAGE, resultLimit, null);
|
||||
},
|
||||
500));
|
||||
|
||||
adapter =
|
||||
new AdminUnadoptedReposAdapter(
|
||||
new ArrayList<>(),
|
||||
() -> {
|
||||
PAGE = 1;
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
reload = true;
|
||||
viewModel.loadRepos(ctx, PAGE, resultLimit, null);
|
||||
},
|
||||
() -> {
|
||||
PAGE += 1;
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
viewModel.loadRepos(ctx, PAGE, resultLimit, null);
|
||||
},
|
||||
binding);
|
||||
|
||||
binding.recyclerView.setAdapter(adapter);
|
||||
|
||||
fetchDataAsync(ctx);
|
||||
}
|
||||
|
||||
private void fetchDataAsync(Context ctx) {
|
||||
|
||||
AtomicInteger prevSize = new AtomicInteger();
|
||||
|
||||
viewModel
|
||||
.getUnadoptedRepos(ctx, PAGE, resultLimit, null)
|
||||
.observe(
|
||||
this,
|
||||
list -> {
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
|
||||
boolean hasMore = reload || list.size() > prevSize.get();
|
||||
reload = false;
|
||||
|
||||
prevSize.set(list.size());
|
||||
|
||||
if (list.size() > 0) {
|
||||
adapter.updateList(list);
|
||||
adapter.setHasMore(hasMore);
|
||||
binding.noData.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.noData.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initCloseListener() {
|
||||
onClickListener = view -> finish();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.gitnex.tea4j.v2.models.Cron;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.adapters.AdminCronTasksAdapter;
|
||||
import org.mian.gitnex.adapters.AdminUnadoptedReposAdapter;
|
||||
import org.mian.gitnex.api.models.settings.RepositoryGlobal;
|
||||
import org.mian.gitnex.databinding.ActivityAdministrationBinding;
|
||||
import org.mian.gitnex.databinding.BottomSheetGlobalRepositorySettingsBinding;
|
||||
@@ -86,10 +87,7 @@ public class AdministrationActivity extends BaseActivity {
|
||||
|
||||
binding.cardCron.getRoot().setOnClickListener(v -> showCronTasksSheet());
|
||||
|
||||
binding.cardUnadopted
|
||||
.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> startActivity(new Intent(this, AdminUnadoptedReposActivity.class)));
|
||||
binding.cardUnadopted.getRoot().setOnClickListener(v -> showUnadoptedReposSheet());
|
||||
|
||||
binding.cardRepoSettings.getRoot().setOnClickListener(v -> showRepositorySettings());
|
||||
}
|
||||
@@ -144,7 +142,7 @@ public class AdministrationActivity extends BaseActivity {
|
||||
});
|
||||
|
||||
viewModel
|
||||
.getIsLoading()
|
||||
.getIsCronLoading()
|
||||
.observe(
|
||||
this,
|
||||
loading ->
|
||||
@@ -211,7 +209,7 @@ public class AdministrationActivity extends BaseActivity {
|
||||
});
|
||||
|
||||
viewModel
|
||||
.getIsLoading()
|
||||
.getIsSettingsLoading()
|
||||
.observe(
|
||||
this,
|
||||
loading -> {
|
||||
@@ -248,6 +246,88 @@ public class AdministrationActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void showUnadoptedReposSheet() {
|
||||
|
||||
BottomsheetAdminCronTasksBinding sheetBinding =
|
||||
BottomsheetAdminCronTasksBinding.inflate(getLayoutInflater());
|
||||
BottomSheetDialog dialog = new BottomSheetDialog(this);
|
||||
dialog.setContentView(sheetBinding.getRoot());
|
||||
AppUtil.applySheetStyle(dialog, true);
|
||||
|
||||
sheetBinding.title.setText(R.string.unadoptedRepos);
|
||||
viewModel.resetUnadoptedPagination();
|
||||
|
||||
AdminUnadoptedReposAdapter adapter =
|
||||
new AdminUnadoptedReposAdapter(new ArrayList<>(), this::showUnadoptedActionDialog);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||
sheetBinding.recyclerView.setLayoutManager(layoutManager);
|
||||
sheetBinding.recyclerView.setAdapter(adapter);
|
||||
|
||||
EndlessRecyclerViewScrollListener scrollListener =
|
||||
new EndlessRecyclerViewScrollListener(layoutManager) {
|
||||
@Override
|
||||
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
|
||||
viewModel.fetchUnadoptedRepos(
|
||||
AdministrationActivity.this, page, resultLimit, false);
|
||||
}
|
||||
};
|
||||
sheetBinding.recyclerView.addOnScrollListener(scrollListener);
|
||||
|
||||
viewModel
|
||||
.getUnadoptedRepos()
|
||||
.observe(
|
||||
this,
|
||||
list -> {
|
||||
adapter.updateList(list);
|
||||
sheetBinding
|
||||
.layoutEmpty
|
||||
.getRoot()
|
||||
.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
});
|
||||
|
||||
viewModel
|
||||
.getIsUnadoptedLoading()
|
||||
.observe(
|
||||
this,
|
||||
loading ->
|
||||
sheetBinding.expressiveLoader.setVisibility(
|
||||
loading ? View.VISIBLE : View.GONE));
|
||||
|
||||
viewModel
|
||||
.getRepoActionSuccess()
|
||||
.observe(
|
||||
this,
|
||||
result -> {
|
||||
if (result != null) {
|
||||
String message;
|
||||
if (result.isDelete()) {
|
||||
message = getString(R.string.repoDeletionSuccess);
|
||||
} else {
|
||||
message = getString(R.string.repoAdopted, result.repoName());
|
||||
}
|
||||
Toasty.show(this, message);
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.fetchUnadoptedRepos(this, 1, resultLimit, true);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void showUnadoptedActionDialog(String repoName) {
|
||||
String[] parts = repoName.split("/");
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(repoName)
|
||||
.setMessage(getString(R.string.unadoptedReposMessage, parts[1], parts[0]))
|
||||
.setNeutralButton(R.string.close, null)
|
||||
.setPositiveButton(
|
||||
R.string.menuDeleteText,
|
||||
(d, w) -> viewModel.performRepoAction(this, repoName, true))
|
||||
.setNegativeButton(
|
||||
R.string.adoptRepo,
|
||||
(d, w) -> viewModel.performRepoAction(this, repoName, false))
|
||||
.show();
|
||||
}
|
||||
|
||||
private void initCards() {
|
||||
binding.cardUsers.cardIcon.setImageResource(R.drawable.ic_people);
|
||||
binding.cardUsers.cardTitle.setText(R.string.adminUsers);
|
||||
|
||||
@@ -1,169 +1,48 @@
|
||||
package org.mian.gitnex.adapters;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import java.util.List;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.databinding.ActivityAdminCronTasksBinding;
|
||||
import org.mian.gitnex.helpers.AlertDialogs;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import org.mian.gitnex.databinding.ListAdminUnadoptedReposBinding;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
* @author qwerty287
|
||||
*/
|
||||
public class AdminUnadoptedReposAdapter
|
||||
extends RecyclerView.Adapter<AdminUnadoptedReposAdapter.UnadoptedViewHolder> {
|
||||
extends RecyclerView.Adapter<AdminUnadoptedReposAdapter.ViewHolder> {
|
||||
|
||||
private final Runnable updateList;
|
||||
private final Runnable loadMoreListener;
|
||||
private final ActivityAdminCronTasksBinding activityAdminCronTasksBinding;
|
||||
private List<String> repos;
|
||||
private boolean isLoading = false, hasMore = true;
|
||||
private final List<String> repos;
|
||||
private final OnRepoClickListener listener;
|
||||
|
||||
public AdminUnadoptedReposAdapter(
|
||||
List<String> list,
|
||||
Runnable updateList,
|
||||
Runnable loadMore,
|
||||
ActivityAdminCronTasksBinding activityAdminCronTasksBinding) {
|
||||
this.repos = list;
|
||||
this.updateList = updateList;
|
||||
this.loadMoreListener = loadMore;
|
||||
this.activityAdminCronTasksBinding = activityAdminCronTasksBinding;
|
||||
public interface OnRepoClickListener {
|
||||
void onRepoClick(String repoName);
|
||||
}
|
||||
|
||||
public AdminUnadoptedReposAdapter(List<String> repos, OnRepoClickListener listener) {
|
||||
this.repos = repos;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull @Override
|
||||
public UnadoptedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.list_admin_unadopted_repos, parent, false);
|
||||
return new UnadoptedViewHolder(v);
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ListAdminUnadoptedReposBinding binding =
|
||||
ListAdminUnadoptedReposBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull UnadoptedViewHolder holder, int position) {
|
||||
if (position >= getItemCount() - 1 && hasMore && !isLoading && loadMoreListener != null) {
|
||||
isLoading = true;
|
||||
loadMoreListener.run();
|
||||
}
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
String repo = repos.get(position);
|
||||
|
||||
String currentItem = repos.get(position);
|
||||
holder.binding.repoName.setText(repo);
|
||||
holder.binding.getRoot().setOnClickListener(v -> listener.onRepoClick(repo));
|
||||
|
||||
holder.repoName = currentItem;
|
||||
holder.name.setText(currentItem);
|
||||
}
|
||||
|
||||
private void updateAdapter(int position) {
|
||||
repos.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, repos.size());
|
||||
}
|
||||
|
||||
private void delete(final Context ctx, final String name) {
|
||||
|
||||
String[] repoSplit = name.split("/");
|
||||
|
||||
Call<Void> call =
|
||||
RetrofitClient.getApiInterface(ctx)
|
||||
.adminDeleteUnadoptedRepository(repoSplit[0], repoSplit[1]);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
|
||||
|
||||
switch (response.code()) {
|
||||
case 204:
|
||||
updateList.run();
|
||||
Toasty.show(ctx, ctx.getString(R.string.repoDeletionSuccess));
|
||||
break;
|
||||
|
||||
case 401:
|
||||
AlertDialogs.authorizationTokenRevokedDialog(ctx);
|
||||
break;
|
||||
|
||||
case 403:
|
||||
Toasty.show(ctx, ctx.getString(R.string.authorizeError));
|
||||
break;
|
||||
|
||||
case 404:
|
||||
Toasty.show(ctx, ctx.getString(R.string.apiNotFound));
|
||||
break;
|
||||
|
||||
default:
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericError));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void adopt(final Context ctx, final String name, int position) {
|
||||
|
||||
String[] repoSplit = name.split("/");
|
||||
|
||||
Call<Void> call =
|
||||
RetrofitClient.getApiInterface(ctx)
|
||||
.adminAdoptRepository(repoSplit[0], repoSplit[1]);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull retrofit2.Response<Void> response) {
|
||||
|
||||
switch (response.code()) {
|
||||
case 204:
|
||||
updateAdapter(position);
|
||||
if (getItemCount() == 0) {
|
||||
activityAdminCronTasksBinding.noData.setVisibility(
|
||||
View.VISIBLE);
|
||||
}
|
||||
Toasty.show(ctx, ctx.getString(R.string.repoAdopted, name));
|
||||
break;
|
||||
|
||||
case 401:
|
||||
AlertDialogs.authorizationTokenRevokedDialog(ctx);
|
||||
break;
|
||||
|
||||
case 403:
|
||||
Toasty.show(ctx, ctx.getString(R.string.authorizeError));
|
||||
break;
|
||||
|
||||
case 404:
|
||||
Toasty.show(ctx, ctx.getString(R.string.apiNotFound));
|
||||
break;
|
||||
|
||||
default:
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericError));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
holder.binding.getRoot().updateAppearance(position, getItemCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,54 +51,18 @@ public class AdminUnadoptedReposAdapter
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void updateList(List<String> list) {
|
||||
this.repos = list;
|
||||
public void updateList(List<String> newList) {
|
||||
this.repos.clear();
|
||||
this.repos.addAll(newList);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setHasMore(boolean hasMore) {
|
||||
this.hasMore = hasMore;
|
||||
isLoading = false;
|
||||
}
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final ListAdminUnadoptedReposBinding binding;
|
||||
|
||||
public class UnadoptedViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView name;
|
||||
private String repoName;
|
||||
|
||||
private UnadoptedViewHolder(View itemView) {
|
||||
|
||||
super(itemView);
|
||||
Context ctx = itemView.getContext();
|
||||
|
||||
name = itemView.findViewById(R.id.repo_name);
|
||||
|
||||
itemView.setOnClickListener(
|
||||
taskInfo -> {
|
||||
String[] repoSplit = repoName.split("/");
|
||||
|
||||
MaterialAlertDialogBuilder materialAlertDialogBuilder =
|
||||
new MaterialAlertDialogBuilder(ctx)
|
||||
.setTitle(repoName)
|
||||
.setMessage(
|
||||
ctx.getString(
|
||||
R.string.unadoptedReposMessage,
|
||||
repoSplit[1],
|
||||
repoSplit[0]))
|
||||
.setNeutralButton(R.string.close, null)
|
||||
.setPositiveButton(
|
||||
R.string.menuDeleteText,
|
||||
((dialog, which) -> delete(ctx, repoName)))
|
||||
.setNegativeButton(
|
||||
R.string.adoptRepo,
|
||||
((dialog, which) ->
|
||||
adopt(
|
||||
ctx,
|
||||
repoName,
|
||||
getBindingAdapterPosition())));
|
||||
|
||||
materialAlertDialogBuilder.create().show();
|
||||
});
|
||||
ViewHolder(ListAdminUnadoptedReposBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.mian.gitnex.viewmodels;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.helpers.AlertDialogs;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
* @author qwerty287
|
||||
*/
|
||||
public class AdminUnadoptedReposViewModel extends ViewModel {
|
||||
|
||||
private MutableLiveData<List<String>> tasksList;
|
||||
|
||||
public LiveData<List<String>> getUnadoptedRepos(
|
||||
Context ctx, int page, int limit, String query) {
|
||||
|
||||
tasksList = new MutableLiveData<>();
|
||||
loadRepos(ctx, page, limit, query);
|
||||
|
||||
return tasksList;
|
||||
}
|
||||
|
||||
public void loadRepos(final Context ctx, final int page, int limit, String query) {
|
||||
|
||||
Call<List<String>> call =
|
||||
RetrofitClient.getApiInterface(ctx).adminUnadoptedList(page, limit, query);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<List<String>> call,
|
||||
@NonNull Response<List<String>> response) {
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
if (page <= 1 || tasksList.getValue() == null) {
|
||||
tasksList.postValue(response.body());
|
||||
} else {
|
||||
List<String> repos =
|
||||
new ArrayList<>(
|
||||
Objects.requireNonNull(tasksList.getValue()));
|
||||
assert response.body() != null;
|
||||
repos.addAll(response.body());
|
||||
tasksList.postValue(repos);
|
||||
}
|
||||
} else if (response.code() == 401) {
|
||||
AlertDialogs.authorizationTokenRevokedDialog(ctx);
|
||||
} else if (response.code() == 403) {
|
||||
Toasty.show(ctx, ctx.getString(R.string.authorizeError));
|
||||
} else if (response.code() == 404) {
|
||||
Toasty.show(ctx, ctx.getString(R.string.apiNotFound));
|
||||
} else {
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericError));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<String>> call, @NonNull Throwable t) {
|
||||
|
||||
Toasty.show(ctx, ctx.getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,20 @@ public class AdministrationViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<RepositoryGlobal> repositorySettings = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<Cron>> cronTasks = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<List<String>> unadoptedRepos =
|
||||
new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
private final MutableLiveData<Boolean> isSettingsLoading = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Boolean> isCronLoading = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Boolean> isUnadoptedLoading = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<String> errorMessage = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> taskSuccessMessage = new MutableLiveData<>();
|
||||
private final MutableLiveData<RepoActionResult> repoActionSuccess = new MutableLiveData<>();
|
||||
|
||||
private int cronTotalCount = -1;
|
||||
private boolean isCronLastPage = false;
|
||||
private int unadoptedTotalCount = -1;
|
||||
private boolean isUnadoptedLastPage = false;
|
||||
|
||||
public LiveData<RepositoryGlobal> getRepositorySettings() {
|
||||
return repositorySettings;
|
||||
@@ -38,8 +46,20 @@ public class AdministrationViewModel extends ViewModel {
|
||||
return cronTasks;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsLoading() {
|
||||
return isLoading;
|
||||
public LiveData<List<String>> getUnadoptedRepos() {
|
||||
return unadoptedRepos;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsSettingsLoading() {
|
||||
return isSettingsLoading;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsCronLoading() {
|
||||
return isCronLoading;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsUnadoptedLoading() {
|
||||
return isUnadoptedLoading;
|
||||
}
|
||||
|
||||
public LiveData<String> getErrorMessage() {
|
||||
@@ -50,8 +70,12 @@ public class AdministrationViewModel extends ViewModel {
|
||||
return taskSuccessMessage;
|
||||
}
|
||||
|
||||
public LiveData<RepoActionResult> getRepoActionSuccess() {
|
||||
return repoActionSuccess;
|
||||
}
|
||||
|
||||
public void fetchRepositoryGlobalSettings(Context context) {
|
||||
isLoading.setValue(true);
|
||||
isSettingsLoading.setValue(true);
|
||||
ApiRetrofitClient.getInstance(context)
|
||||
.getRepositoryGlobalSettings()
|
||||
.enqueue(
|
||||
@@ -60,29 +84,26 @@ public class AdministrationViewModel extends ViewModel {
|
||||
public void onResponse(
|
||||
@NonNull Call<RepositoryGlobal> call,
|
||||
@NonNull Response<RepositoryGlobal> response) {
|
||||
isLoading.setValue(false);
|
||||
if (response.isSuccessful()) {
|
||||
isSettingsLoading.setValue(false);
|
||||
if (response.isSuccessful())
|
||||
repositorySettings.setValue(response.body());
|
||||
} else {
|
||||
errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
else errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(
|
||||
@NonNull Call<RepositoryGlobal> call, @NonNull Throwable t) {
|
||||
isLoading.setValue(false);
|
||||
isSettingsLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchCronTasks(Context context, int page, int limit, boolean isRefresh) {
|
||||
if (Boolean.TRUE.equals(isLoading.getValue())) return;
|
||||
if (Boolean.TRUE.equals(isCronLoading.getValue())) return;
|
||||
if (!isRefresh && isCronLastPage) return;
|
||||
|
||||
isLoading.setValue(true);
|
||||
|
||||
isCronLoading.setValue(true);
|
||||
RetrofitClient.getApiInterface(context)
|
||||
.adminCronList(page, limit)
|
||||
.enqueue(
|
||||
@@ -91,69 +112,155 @@ public class AdministrationViewModel extends ViewModel {
|
||||
public void onResponse(
|
||||
@NonNull Call<List<Cron>> call,
|
||||
@NonNull Response<List<Cron>> response) {
|
||||
isLoading.setValue(false);
|
||||
isCronLoading.setValue(false);
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
String totalHeader = response.headers().get("x-total-count");
|
||||
if (totalHeader != null) {
|
||||
cronTotalCount = Integer.parseInt(totalHeader);
|
||||
}
|
||||
|
||||
List<Cron> currentList =
|
||||
isRefresh
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(
|
||||
Objects.requireNonNull(
|
||||
cronTasks.getValue()));
|
||||
currentList.addAll(response.body());
|
||||
cronTasks.setValue(currentList);
|
||||
|
||||
if (response.body().size() < limit
|
||||
|| currentList.size() >= cronTotalCount) {
|
||||
isCronLastPage = true;
|
||||
}
|
||||
} else {
|
||||
errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
handleCronResponse(
|
||||
response.body(),
|
||||
response.headers().get("x-total-count"),
|
||||
limit,
|
||||
isRefresh);
|
||||
} else errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(
|
||||
@NonNull Call<List<Cron>> call, @NonNull Throwable t) {
|
||||
isLoading.setValue(false);
|
||||
isCronLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleCronResponse(
|
||||
List<Cron> body, String totalHeader, int limit, boolean isRefresh) {
|
||||
if (totalHeader != null) cronTotalCount = Integer.parseInt(totalHeader);
|
||||
List<Cron> currentList =
|
||||
isRefresh
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(Objects.requireNonNull(cronTasks.getValue()));
|
||||
currentList.addAll(body);
|
||||
cronTasks.setValue(currentList);
|
||||
if (body.size() < limit || currentList.size() >= cronTotalCount) isCronLastPage = true;
|
||||
}
|
||||
|
||||
public void runCronTask(Context context, String taskName) {
|
||||
isCronLoading.setValue(true);
|
||||
RetrofitClient.getApiInterface(context)
|
||||
.adminCronRun(taskName)
|
||||
.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull Response<Void> response) {
|
||||
isCronLoading.setValue(false);
|
||||
if (response.code() == 204) {
|
||||
taskSuccessMessage.setValue(taskName);
|
||||
taskSuccessMessage.setValue(null);
|
||||
} else errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
isCronLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchUnadoptedRepos(Context context, int page, int limit, boolean isRefresh) {
|
||||
if (Boolean.TRUE.equals(isUnadoptedLoading.getValue())) return;
|
||||
if (!isRefresh && isUnadoptedLastPage) return;
|
||||
|
||||
isUnadoptedLoading.setValue(true);
|
||||
RetrofitClient.getApiInterface(context)
|
||||
.adminUnadoptedList(page, limit, null)
|
||||
.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<List<String>> call,
|
||||
@NonNull Response<List<String>> response) {
|
||||
isUnadoptedLoading.setValue(false);
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
handleUnadoptedResponse(
|
||||
response.body(),
|
||||
response.headers().get("x-total-count"),
|
||||
limit,
|
||||
isRefresh);
|
||||
} else errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(
|
||||
@NonNull Call<List<String>> call, @NonNull Throwable t) {
|
||||
isUnadoptedLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleUnadoptedResponse(
|
||||
List<String> body, String totalHeader, int limit, boolean isRefresh) {
|
||||
if (totalHeader != null) unadoptedTotalCount = Integer.parseInt(totalHeader);
|
||||
List<String> currentList =
|
||||
isRefresh
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(Objects.requireNonNull(unadoptedRepos.getValue()));
|
||||
currentList.addAll(body);
|
||||
unadoptedRepos.setValue(currentList);
|
||||
if (body.size() < limit || currentList.size() >= unadoptedTotalCount)
|
||||
isUnadoptedLastPage = true;
|
||||
}
|
||||
|
||||
public void performRepoAction(Context context, String repoName, boolean isDelete) {
|
||||
isUnadoptedLoading.setValue(true);
|
||||
String[] parts = repoName.split("/");
|
||||
Call<Void> call =
|
||||
isDelete
|
||||
? RetrofitClient.getApiInterface(context)
|
||||
.adminDeleteUnadoptedRepository(parts[0], parts[1])
|
||||
: RetrofitClient.getApiInterface(context)
|
||||
.adminAdoptRepository(parts[0], parts[1]);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull Response<Void> response) {
|
||||
isUnadoptedLoading.setValue(false);
|
||||
if (response.code() == 204) {
|
||||
List<String> current =
|
||||
new ArrayList<>(
|
||||
Objects.requireNonNull(unadoptedRepos.getValue()));
|
||||
current.remove(repoName);
|
||||
unadoptedRepos.setValue(current);
|
||||
|
||||
repoActionSuccess.setValue(new RepoActionResult(repoName, isDelete));
|
||||
repoActionSuccess.setValue(null);
|
||||
} else {
|
||||
errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
isUnadoptedLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void resetCronPagination() {
|
||||
this.isCronLastPage = false;
|
||||
this.cronTotalCount = -1;
|
||||
this.cronTasks.setValue(new ArrayList<>());
|
||||
}
|
||||
|
||||
public void runCronTask(Context context, String taskName) {
|
||||
isLoading.setValue(true);
|
||||
RetrofitClient.getApiInterface(context)
|
||||
.adminCronRun(taskName)
|
||||
.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull Response<Void> response) {
|
||||
isLoading.setValue(false);
|
||||
if (response.code() == 204) {
|
||||
taskSuccessMessage.setValue(taskName);
|
||||
taskSuccessMessage.setValue(null);
|
||||
} else {
|
||||
errorMessage.setValue("Error: " + response.code());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
isLoading.setValue(false);
|
||||
errorMessage.setValue(t.getMessage());
|
||||
}
|
||||
});
|
||||
public void resetUnadoptedPagination() {
|
||||
this.isUnadoptedLastPage = false;
|
||||
this.unadoptedTotalCount = -1;
|
||||
this.unadoptedRepos.setValue(new ArrayList<>());
|
||||
}
|
||||
|
||||
public record RepoActionResult(String repoName, boolean isDelete) {}
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBackgroundColor"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:elevation="@dimen/dimen0dp"
|
||||
android:theme="@style/Widget.AppCompat.SearchView">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryBackgroundColor">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/close"
|
||||
android:layout_width="@dimen/dimen26dp"
|
||||
android:layout_height="@dimen/dimen26dp"
|
||||
android:layout_marginEnd="@dimen/dimen16dp"
|
||||
android:layout_marginStart="@dimen/dimen16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:contentDescription="@string/close"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:src="@drawable/ic_close" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/toolbarTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/adminCron"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:maxLines="1"
|
||||
android:textSize="@dimen/dimen20sp" />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBackgroundColor"
|
||||
android:layout_marginTop="@dimen/dimen56dp"
|
||||
android:padding="@dimen/dimen8dp">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/pullToRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dimen56dp"
|
||||
android:indeterminate="true"
|
||||
style="@style/Widget.Material3.LinearProgressIndicator"
|
||||
app:indicatorColor="?attr/progressIndicatorColor" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noData"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/dimen16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/noDataFound"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="@dimen/dimen20sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -23,14 +23,28 @@
|
||||
|
||||
<com.google.android.material.loadingindicator.LoadingIndicator
|
||||
android:id="@+id/expressive_loader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dimen24dp"
|
||||
android:layout_marginBottom="@dimen/dimen24dp"
|
||||
android:layout_marginTop="@dimen/dimen48dp"
|
||||
android:layout_marginBottom="@dimen/dimen48dp"
|
||||
android:layout_gravity="center"
|
||||
app:indicatorSize="@dimen/dimen48dp"
|
||||
app:trackThickness="@dimen/dimen4dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<include
|
||||
android:id="@+id/layout_empty"
|
||||
layout="@layout/layout_empty_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dimen32dp"
|
||||
android:layout_marginBottom="@dimen/dimen32dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
@@ -1,35 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
<com.google.android.material.listitem.ListItemLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingTop="@dimen/dimen4dp"
|
||||
android:paddingBottom="@dimen/dimen4dp">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
<com.google.android.material.listitem.ListItemCardView
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/materialCardViewElevatedStyle"
|
||||
app:cardElevation="@dimen/dimen0dp">
|
||||
android:checkable="false"
|
||||
android:layout_marginVertical="@dimen/dimen1dp"
|
||||
app:strokeWidth="0dp"
|
||||
app:cardBackgroundColor="?attr/materialCardBackgroundColor">
|
||||
|
||||
<LinearLayout
|
||||
<TextView
|
||||
android:id="@+id/repo_name"
|
||||
style="@style/TextAppearance.Material3.BodyLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?attr/materialCardBackgroundColor"
|
||||
android:padding="@dimen/dimen12dp"
|
||||
android:orientation="vertical">
|
||||
android:padding="@dimen/dimen8dp"
|
||||
android:textColor="?attr/primaryTextColor" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/repo_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/dimen6dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="@dimen/dimen16sp"/>
|
||||
</com.google.android.material.listitem.ListItemCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.listitem.ListItemLayout>
|
||||
|
||||
Reference in New Issue
Block a user