diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/CreateStatus.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/CreateStatus.java index 52b93809b..6a241aa45 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/CreateStatus.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/CreateStatus.java @@ -48,6 +48,8 @@ public class CreateStatus extends MastodonAPIRequest{ public String quoteId; public ContentType contentType; + public boolean preview; + public static class Poll{ public ArrayList options=new ArrayList<>(); public int expiresIn; 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 7ac7cbc12..1962f5687 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -214,7 +214,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private BackgroundColorSpan overLimitBG; private ForegroundColorSpan overLimitFG; - + public ComposeFragment(){ super(R.layout.toolbar_fragment_with_progressbar); } @@ -807,25 +807,28 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr actionItem.setActionView(wrap); actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - draftsBtn = wrap.findViewById(R.id.drafts_btn); - draftOptionsPopup = new PopupMenu(getContext(), draftsBtn); + draftsBtn=wrap.findViewById(R.id.drafts_btn); + draftOptionsPopup=new PopupMenu(getContext(), draftsBtn); draftOptionsPopup.inflate(R.menu.compose_more); - draftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.draft); - undraftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.undraft); - scheduleMenuItem = draftOptionsPopup.getMenu().findItem(R.id.schedule); - unscheduleMenuItem = draftOptionsPopup.getMenu().findItem(R.id.unschedule); + Menu draftOptionsMenu=draftOptionsPopup.getMenu(); + draftMenuItem=draftOptionsMenu.findItem(R.id.draft); + undraftMenuItem=draftOptionsMenu.findItem(R.id.undraft); + scheduleMenuItem=draftOptionsMenu.findItem(R.id.schedule); + unscheduleMenuItem=draftOptionsMenu.findItem(R.id.unschedule); + draftOptionsMenu.findItem(R.id.preview).setVisible(isInstanceAkkoma()); draftOptionsPopup.setOnMenuItemClickListener(i->{ - int id = i.getItemId(); - if (id == R.id.draft) updateScheduledAt(getDraftInstant()); - else if (id == R.id.schedule) pickScheduledDateTime(); - else if (id == R.id.unschedule || id == R.id.undraft) updateScheduledAt(null); - else navigateToUnsentPosts(); + int id=i.getItemId(); + if(id==R.id.draft) updateScheduledAt(getDraftInstant()); + else if(id==R.id.schedule) pickScheduledDateTime(); + else if(id==R.id.unschedule || id==R.id.undraft) updateScheduledAt(null); + else if(id==R.id.drafts) navigateToUnsentPosts(); + else if(id==R.id.preview) publish(true); return true; }); UiUtils.enablePopupMenuIcons(getContext(), draftOptionsPopup); - publishButton = wrap.findViewById(R.id.publish_btn); - languageButton = wrap.findViewById(R.id.language_btn); + publishButton=wrap.findViewById(R.id.publish_btn); + languageButton=wrap.findViewById(R.id.language_btn); languageButton.setOnClickListener(v->showLanguageAlert()); languageButton.setOnLongClickListener(v->{ if(!getLocalPrefs().bottomEncoding){ @@ -1051,6 +1054,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } private void publish(){ + publish(false); + } + + private void publish(boolean preview){ sendingOverlay=new View(getActivity()); WindowManager.LayoutParams overlayParams=new WindowManager.LayoutParams(); overlayParams.type=WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; @@ -1064,10 +1071,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr publishButton.setEnabled(false); V.setVisibilityAnimated(sendProgress, View.VISIBLE); - mediaViewController.saveAltTextsBeforePublishing(this::actuallyPublish, this::handlePublishError); + mediaViewController.saveAltTextsBeforePublishing( + ()->actuallyPublish(preview), + this::handlePublishError); } - private void actuallyPublish(){ + private void actuallyPublish(boolean preview){ String text=mainEditText.getText().toString(); CreateStatus.Request req=new CreateStatus.Request(); if("bottom".equals(postLang.encoding)){ @@ -1085,6 +1094,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr req.sensitive=sensitive; req.contentType=contentType==ContentType.UNSPECIFIED ? null : contentType; req.scheduledAt=scheduledAt; + req.preview=preview; if(!mediaViewController.isEmpty()){ req.mediaIds=mediaViewController.getAttachmentIDs(); if(editingStatus != null){ @@ -1112,7 +1122,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr Callback resCallback=new Callback<>(){ @Override public void onSuccess(Status result){ - maybeDeleteScheduledPost(() -> { + if(preview){ + openPreview(result); + return; + } + + maybeDeleteScheduledPost(()->{ wm.removeView(sendingOverlay); sendingOverlay=null; if(editingStatus==null || redraftStatus){ @@ -1134,10 +1149,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } E.post(new StatusUpdatedEvent(editedStatus)); } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) { + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()){ Nav.finish(ComposeFragment.this); } - if (getArguments().getBoolean("navigateToStatus", false)) { + if(getArguments().getBoolean("navigateToStatus", false)){ Bundle args=new Bundle(); args.putString("account", accountID); args.putParcelable("status", Parcels.wrap(result)); @@ -1153,11 +1168,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } }; - if(editingStatus!=null && !redraftStatus){ + if(editingStatus!=null && !redraftStatus && !preview){ new EditStatus(req, editingStatus.id) .setCallback(resCallback) .exec(accountID); - }else if(req.scheduledAt == null){ + }else if(req.scheduledAt == null || preview){ new CreateStatus(req, uuid) .setCallback(resCallback) .exec(accountID); @@ -1210,6 +1225,25 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } } + private void openPreview(Status result){ + result.preview=true; + wm.removeView(sendingOverlay); + sendingOverlay=null; + publishButton.setEnabled(true); + V.setVisibilityAnimated(sendProgress, View.GONE); + InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class); + imm.hideSoftInputFromWindow(contentView.getWindowToken(), 0); + + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("status", Parcels.wrap(result)); + if(replyTo!=null){ + args.putParcelable("inReplyTo", Parcels.wrap(replyTo)); + args.putParcelable("inReplyToAccount", Parcels.wrap(replyTo.account)); + } + Nav.go(getActivity(), ThreadFragment.class, args); + } + private void updateRecentLanguages() { if (postLang == null || postLang.language == null) return; String language = postLang.language.getLanguage(); @@ -1528,6 +1562,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } contentTypePopup.setOnMenuItemClickListener(i->{ + uuid=null; int index=i.getItemId(); contentType=ContentType.values()[index]; btn.setSelected(index!=ContentType.UNSPECIFIED.ordinal() && index!=ContentType.PLAIN.ordinal()); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index a9ce45dff..c9d238766 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -84,8 +84,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment @Override public void onItemClick(String id){ Status status=getContentStatusByID(id); - if(status==null) - return; + if(status==null || status.preview) return; status.filterRevealed=true; Bundle args=new Bundle(); args.putString("account", accountID); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 654636444..e85cad37b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -50,22 +50,25 @@ import me.grishka.appkit.api.SimpleCallback; import me.grishka.appkit.utils.V; public class ThreadFragment extends StatusListFragment implements ProvidesAssistContent { - protected Status mainStatus, updatedStatus; + protected Status mainStatus, updatedStatus, replyTo; private final HashMap ancestryMap = new HashMap<>(); private StatusContext result; - protected boolean contextInitiallyRendered, transitionFinished; + protected boolean contextInitiallyRendered, transitionFinished, preview; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); mainStatus=Parcels.unwrap(getArguments().getParcelable("status")); + replyTo=Parcels.unwrap(getArguments().getParcelable("inReplyTo")); Account inReplyToAccount=Parcels.unwrap(getArguments().getParcelable("inReplyToAccount")); refreshing=contextInitiallyRendered=getArguments().getBoolean("refresh", false); if(inReplyToAccount!=null) knownAccounts.put(inReplyToAccount.id, inReplyToAccount); data.add(mainStatus); onAppendItems(Collections.singletonList(mainStatus)); - setTitle(HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.getDisplayName()), mainStatus.account.emojis)); + preview=mainStatus.preview; + if(preview) setRefreshEnabled(false); + setTitle(preview ? getString(R.string.sk_post_preview) : HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.getDisplayName()), mainStatus.account.emojis)); transitionFinished = getArguments().getBoolean("noTransition", false); } @@ -130,11 +133,21 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override protected void doLoadData(int offset, int count){ - if (refreshing) loadMainStatus(); - currentRequest=new GetStatusContext(mainStatus.id) + if(preview && replyTo==null){ + result=new StatusContext(); + result.descendants=Collections.emptyList(); + result.ancestors=Collections.emptyList(); + return; + } + if(refreshing && !preview) loadMainStatus(); + currentRequest=new GetStatusContext(preview ? replyTo.id : mainStatus.id) .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(StatusContext result){ + if(preview){ + result.descendants=Collections.emptyList(); + result.ancestors.add(replyTo); + } ThreadFragment.this.result = result; maybeApplyContext(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 01b2ce29c..6f94eaf5f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -97,6 +97,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ public transient TranslationState translationState=TranslationState.HIDDEN; public transient Translation translation; public transient boolean fromStatusCreated; + public transient boolean preview; public Status(){} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java index 7e7612e1c..3e08f6101 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java @@ -129,6 +129,7 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{ } private void startAccountListFragment(Class cls){ + if(item.status.preview) return; Bundle args=new Bundle(); args.putString("account", item.parentFragment.getAccountID()); args.putParcelable("status", Parcels.wrap(item.status)); @@ -136,6 +137,7 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{ } private void startEditHistoryFragment(){ + if(item.status.preview) return; Bundle args=new Bundle(); args.putString("account", item.parentFragment.getAccountID()); args.putString("id", item.status.id); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index 9d1695084..6e343be51 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -158,6 +158,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onButtonTouch(View v, MotionEvent event){ + if(item.status.preview) return false; boolean disabled = !v.isEnabled() || (v instanceof FrameLayout parentFrame && parentFrame.getChildCount() > 0 && !parentFrame.getChildAt(0).isEnabled()); int action = event.getAction(); @@ -180,6 +181,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onReplyClick(View v){ + if(item.status.preview) return; UiUtils.opacityIn(v); Bundle args=new Bundle(); args.putString("account", item.accountID); @@ -188,6 +190,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onReplyLongClick(View v) { + if(item.status.preview) return false; if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false; UiUtils.pickAccount(v.getContext(), item.accountID, R.string.sk_reply_as, R.drawable.ic_fluent_arrow_reply_28_regular, session -> { Bundle args=new Bundle(); @@ -203,6 +206,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onBoostClick(View v){ + if(item.status.preview) return; if (GlobalUserPreferences.confirmBoost) { UiUtils.opacityIn(v); onBoostLongClick(v); @@ -218,6 +222,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onBoostLongClick(View v){ + if(item.status.preview) return false; Context ctx = itemView.getContext(); View menu = LayoutInflater.from(ctx).inflate(R.layout.item_boost_menu, null); Dialog dialog = new M3AlertDialogBuilder(ctx).setView(menu).create(); @@ -300,6 +305,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onFavoriteClick(View v){ + if(item.status.preview) return; favorite.setSelected(!item.status.favourited); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{ UiUtils.opacityIn(v); @@ -308,6 +314,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onFavoriteLongClick(View v) { + if(item.status.preview) return false; if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false; UiUtils.pickInteractAs(v.getContext(), item.accountID, item.status, @@ -322,6 +329,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onBookmarkClick(View v){ + if(item.status.preview) return; bookmark.setSelected(!item.status.bookmarked); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{ UiUtils.opacityIn(v); @@ -329,6 +337,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onBookmarkLongClick(View v) { + if(item.status.preview) return false; if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false; UiUtils.pickInteractAs(v.getContext(), item.accountID, item.status, @@ -343,6 +352,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onShareClick(View v){ + if(item.status.preview) return; UiUtils.opacityIn(v); Intent intent=new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); @@ -351,6 +361,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private boolean onShareLongClick(View v){ + if(item.status.preview) return false; UiUtils.copyText(v, item.status.url); return true; } 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 5bda18b06..0d346608c 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 @@ -445,6 +445,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ } private void onMoreClick(View v){ + if(item.status.preview) return; updateOptionsMenu(); optionsMenu.show(); if(relationship==null && currentRelationshipRequest==null){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 08943d3c0..5f9835126 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -76,7 +76,7 @@ public abstract class StatusDisplayItem{ public static final int FLAG_NO_TRANSLATE=1 << 5; public static final int FLAG_NO_EMOJI_REACTIONS=1 << 6; public static final int FLAG_IS_FOR_QUOTE=1 << 7; - + public void setAncestryInfo( boolean hasDescendantNeighbor, boolean hasAncestoringNeighbor, @@ -166,7 +166,7 @@ public abstract class StatusDisplayItem{ HeaderStatusDisplayItem header=null; boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts; - + if((flags & FLAG_NO_HEADER)==0){ ReblogOrReplyLineStatusDisplayItem replyLine = null; boolean threadReply = statusForContent.inReplyToAccountId != null && @@ -305,7 +305,7 @@ public abstract class StatusDisplayItem{ items.addAll(contentItems); } AccountLocalPreferences lp=fragment.getLocalPrefs(); - if((flags & FLAG_NO_EMOJI_REACTIONS)==0 && lp.emojiReactionsEnabled && + if((flags & FLAG_NO_EMOJI_REACTIONS)==0 && !status.preview && lp.emojiReactionsEnabled && (lp.showEmojiReactions!=ONLY_OPENED || fragment instanceof ThreadFragment) && statusForContent.reactions!=null){ boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id); diff --git a/mastodon/src/main/res/drawable/ic_fluent_receipt_sparkles_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_receipt_sparkles_24_regular.xml new file mode 100644 index 000000000..80cd0471c --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_receipt_sparkles_24_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/menu/compose_more.xml b/mastodon/src/main/res/menu/compose_more.xml index a47109276..3671739ff 100644 --- a/mastodon/src/main/res/menu/compose_more.xml +++ b/mastodon/src/main/res/menu/compose_more.xml @@ -5,4 +5,5 @@ + diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index fcb8f04ad..f707744a2 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -433,4 +433,6 @@ Copy latest crash log None available… yet Crash log copied + Preview post + Preview