diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index 22a8cf783..83118f17b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -22,6 +22,7 @@ import android.text.Layout; import android.text.Spanned; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.format.DateUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -53,6 +54,7 @@ import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.R; import org.joinmastodon.android.api.ProgressListener; import org.joinmastodon.android.api.requests.statuses.CreateStatus; +import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.requests.statuses.UploadAttachment; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; @@ -83,9 +85,11 @@ import org.joinmastodon.android.ui.views.SizeListenerLinearLayout; import org.parceler.Parcel; import org.parceler.Parcels; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -174,6 +178,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private Instance instance; private boolean attachmentsErrorShowing; + public static DraftMediaAttachment redraftAttachment(Attachment att) { + DraftMediaAttachment draft=new DraftMediaAttachment(); + draft.serverAttachment=att; + draft.description=att.description; + draft.uri=Uri.parse(att.url); + return draft; + } + @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); @@ -286,11 +298,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr pollDurationView.setOnClickListener(v->showPollDurationMenu()); pollOptions.clear(); - if(savedInstanceState!=null && savedInstanceState.containsKey("pollOptions")){ + ArrayList restoredPollOptions=(savedInstanceState!=null ? savedInstanceState : getArguments()) + .getStringArrayList("pollOptions"); + if(restoredPollOptions!=null){ + if(savedInstanceState==null){ + // restoring from arguments + pollDuration=getArguments().getInt("pollDuration"); + pollDurationStr=DateUtils.formatElapsedTime(pollDuration); // getResources().getQuantityString(R.plurals.x_hours, pollDuration/3600); + } pollBtn.setSelected(true); mediaBtn.setEnabled(false); pollWrap.setVisibility(View.VISIBLE); - for(String oldText:savedInstanceState.getStringArrayList("pollOptions")){ + for(String oldText:restoredPollOptions){ DraftPollOption opt=createDraftPollOption(); opt.edit.setText(oldText); } @@ -310,8 +329,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr spoilerBtn.setSelected(true); } - if(savedInstanceState!=null && savedInstanceState.containsKey("attachments")){ - ArrayList serializedAttachments=savedInstanceState.getParcelableArrayList("attachments"); + ArrayList serializedAttachments=(savedInstanceState!=null ? savedInstanceState : getArguments()) + .getParcelableArrayList("attachments"); + if(serializedAttachments!=null){ for(Parcelable a:serializedAttachments){ DraftMediaAttachment att=Parcels.unwrap(a); attachmentsView.addView(createMediaAttachmentView(att)); @@ -456,10 +476,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr // TODO: setting for preserving cw always / only when replying to own posts // && AccountSessionManager.getInstance().isSelf(accountID, replyTo.account) if(!TextUtils.isEmpty(replyTo.spoilerText)){ + insertSpoiler(replyTo.spoilerText); hasSpoiler=true; - spoilerEdit.setVisibility(View.VISIBLE); - spoilerEdit.setText(replyTo.spoilerText); - spoilerBtn.setSelected(true); } } }else{ @@ -472,6 +490,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr mainEditText.setSelection(mainEditText.length()); initialText=prefilledText; } + String spoilerText=getArguments().getString("spoilerText"); + if(!TextUtils.isEmpty(spoilerText)) insertSpoiler(spoilerText); ArrayList mediaUris=getArguments().getParcelableArrayList("mediaAttachments"); if(mediaUris!=null && !mediaUris.isEmpty()){ for(Uri uri:mediaUris){ @@ -481,6 +501,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } } + private void insertSpoiler(String text) { + hasSpoiler=true; + if (text!=null) spoilerEdit.setText(text); + spoilerEdit.setVisibility(View.VISIBLE); + spoilerBtn.setSelected(true); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ publishButton=new Button(getActivity()); @@ -549,8 +576,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr if(opt.edit.length()>0) nonEmptyPollOptionsCount++; } - publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && uploadingAttachment==null && failedAttachments.isEmpty() && queuedAttachments.isEmpty() - && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1)); + if(publishButton!=null){ + publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit + && uploadingAttachment==null && failedAttachments.isEmpty() && queuedAttachments.isEmpty() + && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1)); + } } private void onCustomEmojiClick(Emoji emoji){ @@ -637,8 +667,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr boolean pollFieldsHaveContent=false; for(DraftPollOption opt:pollOptions) pollFieldsHaveContent|=opt.edit.length()>0; - return (mainEditText.length()>0 && !mainEditText.getText().toString().equals(initialText)) || !attachments.isEmpty() - || uploadingAttachment!=null || !queuedAttachments.isEmpty() || !failedAttachments.isEmpty() || pollFieldsHaveContent; + return getArguments().getBoolean("hasDraft", false) + || (mainEditText.length()>0 && !mainEditText.getText().toString().equals(initialText)) + || !attachments.isEmpty() || uploadingAttachment!=null || !queuedAttachments.isEmpty() + || !failedAttachments.isEmpty() || pollFieldsHaveContent; } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index 7c70621de..509b3abcf 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -139,6 +139,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{}); }else if(id==R.id.pin || id==R.id.unpin){ UiUtils.confirmPinPost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, !item.status.pinned, s->{}); + }else if(id==R.id.delete_and_redraft) { + UiUtils.confirmDeleteAndRedraftPost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{}); }else if(id==R.id.mute){ UiUtils.confirmToggleMuteUser(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), account, relationship!=null && relationship.muting, r->{}); }else if(id==R.id.block){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 229f1c771..3bbb19d7c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -19,6 +19,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.Parcelable; import android.provider.OpenableColumns; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -46,6 +47,8 @@ import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.events.StatusDeletedEvent; import org.joinmastodon.android.events.StatusUnpinnedEvent; +import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.fragments.ComposeFragment; import org.joinmastodon.android.fragments.HashtagTimelineFragment; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.ThreadFragment; @@ -55,6 +58,7 @@ import org.joinmastodon.android.model.Relationship; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.M3AlertDialogBuilder; import org.joinmastodon.android.ui.text.CustomEmojiSpan; +import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.text.SpacerSpan; import org.parceler.Parcels; @@ -64,6 +68,8 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -377,7 +383,62 @@ public class UiUtils{ }) .wrapProgress(activity, pinned ? R.string.pinning : R.string.unpinning, false) .exec(accountID); - }); + } + ); + } + + public static void confirmDeleteAndRedraftPost(Activity activity, String accountID, Status status, Consumer resultCallback){ + showConfirmationAlert(activity, R.string.confirm_delete_and_redraft_title, R.string.confirm_delete_and_redraft, R.string.delete_and_redraft, ()->{ + new DeleteStatus(status.id) + .setCallback(new Callback<>(){ + @Override + public void onSuccess(Status result){ + resultCallback.accept(result); + AccountSessionManager.getInstance().getAccount(accountID).getCacheController().deleteStatus(status.id); + E.post(new StatusDeletedEvent(status.id, accountID)); + UiUtils.redraftStatus(status, accountID, activity); + } + + @Override + public void onError(ErrorResponse error){ + error.showToast(activity); + } + }) + .wrapProgress(activity, R.string.deleting, false) + .exec(accountID); + }); + } + + public static void redraftStatus(Status status, String accountID, Activity activity) { + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putBoolean("hasDraft", true); + args.putString("prefilledText", HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, accountID).toString()); + args.putString("spoilerText", status.spoilerText); + if(status.poll!=null){ + args.putInt("pollDuration", (int)status.poll.expiresAt.minus(status.createdAt.getEpochSecond(), ChronoUnit.SECONDS).getEpochSecond()); + ArrayList opts=status.poll.options.stream().map(o -> o.title).collect(Collectors.toCollection(ArrayList::new)); + args.putStringArrayList("pollOptions", opts); + } + if(!status.mediaAttachments.isEmpty()){ + ArrayList serializedAttachments=status.mediaAttachments.stream() + .map(att -> Parcels.wrap(ComposeFragment.redraftAttachment(att))) + .collect(Collectors.toCollection(ArrayList::new)); + args.putParcelableArrayList("attachments", serializedAttachments); + } + Callback cb=new Callback<>(){ + @Override public void onError(ErrorResponse error) { + onSuccess(null); + error.showToast(activity); + } + @Override public void onSuccess(Status status) { + if (status!=null) args.putParcelable("replyTo", Parcels.wrap(status)); + Nav.go(activity, ComposeFragment.class, args); + } + }; + + if(status.inReplyToId!=null) new GetStatusByID(status.inReplyToId).setCallback(cb).exec(accountID); + else cb.onSuccess(null); } public static void setRelationshipToActionButton(Relationship relationship, Button button){ diff --git a/mastodon/src/main/res/menu/post.xml b/mastodon/src/main/res/menu/post.xml index cc620956f..00724259f 100644 --- a/mastodon/src/main/res/menu/post.xml +++ b/mastodon/src/main/res/menu/post.xml @@ -1,6 +1,7 @@ + diff --git a/mastodon/src/main/res/values-de-rDE/strings.xml b/mastodon/src/main/res/values-de-rDE/strings.xml index ce6b98ec1..0f9c97be9 100644 --- a/mastodon/src/main/res/values-de-rDE/strings.xml +++ b/mastodon/src/main/res/values-de-rDE/strings.xml @@ -343,4 +343,10 @@ %,d Reblog %,d Reblogs + Löschen und neu erstellen + L + Beitrag löschen und neu erstellen + B + Bist du dir sicher, dass du den Beitrag löschen und neu erstellen möchtest? + Bist du dir sicher, dass du den Beitrag löschen möchtest? diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index 33c3eaab0..3cd18f238 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -127,8 +127,11 @@ Vote Tap to reveal Delete + Delete and re-draft Delete Post + Delete and re-draft Post Are you sure you want to delete this post? + Are you sure you want to delete and re-draft this post? Deleting… Pin to profile Pin post to profile