Add delete option in post view. Fixes 1 part of https://github.com/austinhuang0131/barinsta/issues/289

This commit is contained in:
Ammar Githam 2021-03-21 01:14:57 +09:00
parent 7b578e0462
commit 839e30a4e5
7 changed files with 162 additions and 16 deletions

View File

@ -24,7 +24,6 @@ import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -132,8 +131,10 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
private PostViewV2ViewModel viewModel; private PostViewV2ViewModel viewModel;
private PopupMenu optionsPopup; private PopupMenu optionsPopup;
private EditTextDialogFragment editTextDialogFragment; private EditTextDialogFragment editTextDialogFragment;
private boolean wasDeleted;
private MutableLiveData<Object> backStackSavedStateResultLiveData; private MutableLiveData<Object> backStackSavedStateResultLiveData;
private OnDeleteListener onDeleteListener;
private final Observer<Object> backStackSavedStateObserver = result -> { private final Observer<Object> backStackSavedStateObserver = result -> {
if (result == null) return; if (result == null) return;
if (result instanceof String) { if (result instanceof String) {
@ -196,6 +197,15 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
this.onShowListener = onShowListener; this.onShowListener = onShowListener;
} }
public void setOnDeleteListener(final OnDeleteListener onDeleteListener) {
if (onDeleteListener == null) return;
this.onDeleteListener = onDeleteListener;
}
public interface OnDeleteListener {
void onDelete();
}
public static class Builder { public static class Builder {
private final Media feedModel; private final Media feedModel;
private View profilePicElement; private View profilePicElement;
@ -540,7 +550,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
viewModel.getSaved().observe(getViewLifecycleOwner(), this::setSavedResources); viewModel.getSaved().observe(getViewLifecycleOwner(), this::setSavedResources);
viewModel.getOptions().observe(getViewLifecycleOwner(), options -> binding.getRoot().post(() -> { viewModel.getOptions().observe(getViewLifecycleOwner(), options -> binding.getRoot().post(() -> {
setupOptions(options != null && !options.isEmpty()); setupOptions(options != null && !options.isEmpty());
createOptionsPopupMenu(options); createOptionsPopupMenu();
})); }));
} }
@ -1375,30 +1385,73 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
}); });
} }
private void createOptionsPopupMenu(final List<Integer> options) { private void createOptionsPopupMenu() {
if (options == null) return;
if (optionsPopup == null) { if (optionsPopup == null) {
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle); final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.popupMenuStyle);
optionsPopup = new PopupMenu(themeWrapper, binding.options); optionsPopup = new PopupMenu(themeWrapper, binding.options);
} else {
optionsPopup.getMenu().clear();
} }
optionsPopup.getMenuInflater().inflate(R.menu.post_view_menu, optionsPopup.getMenu()); optionsPopup.getMenuInflater().inflate(R.menu.post_view_menu, optionsPopup.getMenu());
final Menu menu = optionsPopup.getMenu(); // final Menu menu = optionsPopup.getMenu();
final int size = menu.size(); // final int size = menu.size();
for (int i = 0; i < size; i++) { // for (int i = 0; i < size; i++) {
final MenuItem item = menu.getItem(i); // final MenuItem item = menu.getItem(i);
if (item == null) continue; // if (item == null) continue;
if (options.contains(item.getItemId())) continue; // if (options.contains(item.getItemId())) continue;
menu.removeItem(item.getItemId()); // menu.removeItem(item.getItemId());
} // }
optionsPopup.setOnMenuItemClickListener(item -> { optionsPopup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == R.id.edit_caption) { if (itemId == R.id.edit_caption) {
showCaptionEditDialog(); showCaptionEditDialog();
return true;
}
if (itemId == R.id.delete) {
item.setEnabled(false);
final LiveData<Resource<Object>> resourceLiveData = viewModel.delete();
handleDeleteResource(resourceLiveData, item);
} }
return true; return true;
}); });
} }
private void handleDeleteResource(final LiveData<Resource<Object>> resourceLiveData, final MenuItem item) {
if (resourceLiveData == null) return;
resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {
@Override
public void onChanged(final Resource<Object> resource) {
try {
switch (resource.status) {
case SUCCESS:
wasDeleted = true;
if (onDeleteListener != null) {
onDeleteListener.onDelete();
}
break;
case ERROR:
if (item != null) {
item.setEnabled(true);
}
final Snackbar snackbar = Snackbar.make(binding.getRoot(),
R.string.delete_unsuccessful,
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.ok, null);
snackbar.show();
break;
case LOADING:
if (item != null) {
item.setEnabled(false);
}
break;
}
} finally {
resourceLiveData.removeObserver(this);
}
}
});
}
private void showCaptionEditDialog() { private void showCaptionEditDialog() {
final Caption caption = viewModel.getCaption().getValue(); final Caption caption = viewModel.getCaption().getValue();
final String captionText = caption != null ? caption.getText() : null; final String captionText = caption != null ? caption.getText() : null;
@ -1561,4 +1614,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
} }
return navController; return navController;
} }
public boolean wasDeleted() {
return wasDeleted;
}
} }

View File

@ -258,7 +258,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
builder.setSharedProfilePicElement(profilePicView) builder.setSharedProfilePicElement(profilePicView)
.setSharedMainPostElement(mainPostImage); .setSharedMainPostElement(mainPostImage);
} }
builder.build().show(getChildFragmentManager(), "post_view"); final PostViewV2Fragment postViewV2Fragment = builder.build();
postViewV2Fragment.setOnDeleteListener(() -> {
postViewV2Fragment.dismiss();
binding.postsRecyclerView.refresh();
});
postViewV2Fragment.show(getChildFragmentManager(), "post_view");
} }
}; };
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() { private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
@ -1086,7 +1091,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername());
profileDetailsBinding.btnDM.setEnabled(true); profileDetailsBinding.btnDM.setEnabled(true);
}).execute(); }).execute();
}); });
profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { profileDetailsBinding.mainProfileImage.setOnClickListener(v -> {
if (!hasStories) { if (!hasStories) {
// show profile pic // show profile pic
@ -1160,7 +1165,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
private void updateSwipeRefreshState() { private void updateSwipeRefreshState() {
Log.d("austin_debug", "usrs: "+binding.postsRecyclerView.isFetching()); Log.d("austin_debug", "usrs: " + binding.postsRecyclerView.isFetching());
binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching()); binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching());
} }

View File

@ -11,6 +11,7 @@ import retrofit2.http.GET;
import retrofit2.http.Header; import retrofit2.http.Header;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;
public interface MediaRepository { public interface MediaRepository {
@ -60,4 +61,15 @@ public interface MediaRepository {
Call<String> uploadFinish(@Header("retry_context") final String retryContext, Call<String> uploadFinish(@Header("retry_context") final String retryContext,
@QueryMap Map<String, String> queryParams, @QueryMap Map<String, String> queryParams,
@FieldMap final Map<String, String> signedForm); @FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/delete/")
Call<String> delete(@Path("mediaId") final String mediaId,
@Query("media_type") final String mediaType,
@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/media/{mediaId}/archive/")
Call<String> archive(@Path("mediaId") final String mediaId,
@FieldMap final Map<String, String> signedForm);
} }

View File

@ -24,6 +24,9 @@ import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.MediaService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -75,6 +78,7 @@ public class PostViewV2ViewModel extends ViewModel {
final ImmutableList.Builder<Integer> builder = ImmutableList.builder(); final ImmutableList.Builder<Integer> builder = ImmutableList.builder();
if (isLoggedIn && media.getUser() != null && media.getUser().getPk() == viewerId) { if (isLoggedIn && media.getUser() != null && media.getUser().getPk() == viewerId) {
builder.add(R.id.edit_caption); builder.add(R.id.edit_caption);
builder.add(R.id.delete);
} }
options.postValue(builder.build()); options.postValue(builder.build());
} }
@ -290,4 +294,36 @@ public class PostViewV2ViewModel extends ViewModel {
public void setViewCount(final Long viewCount) { public void setViewCount(final Long viewCount) {
this.viewCount.postValue(viewCount); this.viewCount.postValue(viewCount);
} }
public LiveData<Resource<Object>> delete() {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
final Call<String> request = mediaService.delete(media.getId(), media.getMediaType());
if (request == null) {
data.postValue(Resource.success(new Object()));
return data;
}
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) {
data.postValue(Resource.error(R.string.generic_null_response, null));
return;
}
final String body = response.body();
if (body == null) {
data.postValue(Resource.error(R.string.generic_null_response, null));
return;
}
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
} }

View File

@ -5,6 +5,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.json.JSONException; import org.json.JSONException;
@ -17,6 +18,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.MediaRepository; import awais.instagrabber.repositories.MediaRepository;
import awais.instagrabber.repositories.requests.UploadFinishOptions; import awais.instagrabber.repositories.requests.UploadFinishOptions;
import awais.instagrabber.repositories.responses.LikersResponse; import awais.instagrabber.repositories.responses.LikersResponse;
@ -33,6 +35,9 @@ import retrofit2.Retrofit;
public class MediaService extends BaseService { public class MediaService extends BaseService {
private static final String TAG = "MediaService"; private static final String TAG = "MediaService";
private static final List<MediaItemType> DELETABLE_ITEMS_TYPES = ImmutableList.of(MediaItemType.MEDIA_TYPE_IMAGE,
MediaItemType.MEDIA_TYPE_VIDEO,
MediaItemType.MEDIA_TYPE_SLIDER);
private final MediaRepository repository; private final MediaRepository repository;
private final String deviceUuid, csrfToken; private final String deviceUuid, csrfToken;
@ -444,4 +449,31 @@ public class MediaService extends BaseService {
final Map<String, String> signedForm = Utils.sign(formBuilder.build()); final Map<String, String> signedForm = Utils.sign(formBuilder.build());
return repository.uploadFinish(MediaUploadHelper.getRetryContextString(), queryMap, signedForm); return repository.uploadFinish(MediaUploadHelper.getRetryContextString(), queryMap, signedForm);
} }
public Call<String> delete(@NonNull final String postId,
@NonNull final MediaItemType type) {
if (!DELETABLE_ITEMS_TYPES.contains(type)) return null;
final Map<String, Object> form = new HashMap<>();
form.put("_csrftoken", csrfToken);
form.put("_uid", userId);
form.put("_uuid", deviceUuid);
form.put("igtv_feed_preview", "false");
form.put("media_id", postId);
final Map<String, String> signedForm = Utils.sign(form);
final String mediaType;
switch (type) {
case MEDIA_TYPE_IMAGE:
mediaType = "PHOTO";
break;
case MEDIA_TYPE_VIDEO:
mediaType = "VIDEO";
break;
case MEDIA_TYPE_SLIDER:
mediaType = "CAROUSEL";
break;
default:
return null;
}
return repository.delete(postId, mediaType, signedForm);
}
} }

View File

@ -3,4 +3,7 @@
<item <item
android:id="@+id/edit_caption" android:id="@+id/edit_caption"
android:title="@string/edit_caption" /> android:title="@string/edit_caption" />
<item
android:id="@+id/delete"
android:title="@string/delete" />
</menu> </menu>

View File

@ -467,4 +467,5 @@
<string name="generic_not_ok_response">Response status is not ok!</string> <string name="generic_not_ok_response">Response status is not ok!</string>
<string name="generic_failed_request">Request failed!</string> <string name="generic_failed_request">Request failed!</string>
<string name="marked_as_seen">Marked as seen</string> <string name="marked_as_seen">Marked as seen</string>
<string name="delete_unsuccessful">Delete unsuccessful</string>
</resources> </resources>