From d844a77e65966c1ffe84b3539eeca375de298f74 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 11 May 2022 17:25:00 +0200 Subject: [PATCH 1/2] add ui items for redraft --- .../android/ui/displayitems/HeaderStatusDisplayItem.java | 9 +++++++++ mastodon/src/main/res/menu/post.xml | 1 + mastodon/src/main/res/values/strings.xml | 1 + 3 files changed, 11 insertions(+) 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 468abe32f..563f603a5 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 @@ -23,6 +23,7 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.fragments.ComposeFragment; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment; import org.joinmastodon.android.model.Account; @@ -137,6 +138,14 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ int id=menuItem.getItemId(); if(id==R.id.delete){ UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{}); + }else if(id==R.id.delete_and_redraft) { + UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{ + Bundle args=new Bundle(); + args.putString("account", item.parentFragment.getAccountID()); + args.putString("prefilledText", HtmlParser.parse(item.status.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID()).toString()); // demo + // TODO: restore re-drafted text and attachments + Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args); + }); }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/res/menu/post.xml b/mastodon/src/main/res/menu/post.xml index 877bd3e26..190110691 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/strings.xml b/mastodon/src/main/res/values/strings.xml index 939ac102c..4721df14a 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -339,4 +339,5 @@ %,d reblogs %1$s via %2$s + Delete and re-draft \ No newline at end of file From 3a4d13b1c6b9ab6c06d19395db505dcf48caf5f9 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 26 May 2022 19:19:42 +0200 Subject: [PATCH 2/2] implement deleting and re-drafting --- .../android/fragments/ComposeFragment.java | 55 +++++++++++++---- .../displayitems/HeaderStatusDisplayItem.java | 9 +-- .../android/ui/utils/UiUtils.java | 60 +++++++++++++++++++ .../src/main/res/values-de-rDE/strings.xml | 6 ++ mastodon/src/main/res/values/strings.xml | 3 + 5 files changed, 113 insertions(+), 20 deletions(-) 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 3ca595a86..399e8f81d 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)); @@ -454,10 +474,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr mainEditText.setText(initialText); mainEditText.setSelection(mainEditText.length()); if(!TextUtils.isEmpty(replyTo.spoilerText) && AccountSessionManager.getInstance().isSelf(accountID, replyTo.account)){ - hasSpoiler=true; - spoilerEdit.setVisibility(View.VISIBLE); - spoilerEdit.setText(replyTo.spoilerText); - spoilerBtn.setSelected(true); + insertSpoiler(replyTo.spoilerText); } } }else{ @@ -470,6 +487,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){ @@ -479,6 +498,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()); @@ -547,8 +573,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){ @@ -635,8 +664,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 563f603a5..64961ad7a 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 @@ -23,7 +23,6 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.BaseStatusListFragment; -import org.joinmastodon.android.fragments.ComposeFragment; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment; import org.joinmastodon.android.model.Account; @@ -139,13 +138,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ if(id==R.id.delete){ UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{}); }else if(id==R.id.delete_and_redraft) { - UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{ - Bundle args=new Bundle(); - args.putString("account", item.parentFragment.getAccountID()); - args.putString("prefilledText", HtmlParser.parse(item.status.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID()).toString()); // demo - // TODO: restore re-drafted text and attachments - Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args); - }); + 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 e40a4a7eb..5c7cefa94 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 @@ -20,6 +20,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; @@ -44,6 +45,8 @@ import org.joinmastodon.android.api.requests.statuses.DeleteStatus; import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.events.StatusDeletedEvent; +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; @@ -53,6 +56,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; @@ -62,6 +66,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; @@ -352,6 +358,60 @@ public class UiUtils{ }); } + 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){ boolean secondaryStyle; if(relationship.blocking){ diff --git a/mastodon/src/main/res/values-de-rDE/strings.xml b/mastodon/src/main/res/values-de-rDE/strings.xml index 4d3fd1493..618acaa66 100644 --- a/mastodon/src/main/res/values-de-rDE/strings.xml +++ b/mastodon/src/main/res/values-de-rDE/strings.xml @@ -332,4 +332,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 813814edf..afda1737c 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -126,8 +126,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… Audio playback Play