diff --git a/app/src/main/java/com/keylesspalace/tusky/SavedTootActivity.java b/app/src/main/java/com/keylesspalace/tusky/SavedTootActivity.java index 78437c802..168e5dc0e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SavedTootActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/SavedTootActivity.java @@ -171,7 +171,8 @@ public final class SavedTootActivity extends BaseActivity implements SavedTootAd /*mentionedUsernames*/null, item.getInReplyToId(), /*quoteId*/null, - /*quoteUrl*/null, + /*quoteStatusAuthor*/null, + /*quoteStatusContent*/null, /*replyVisibility*/null, item.getVisibility(), item.getContentWarning(), diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index 4fabd6c2c..e7870d2f6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -255,7 +255,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter { statusDisplayOptions.showBotOverlay(), statusDisplayOptions.useBlurhash(), CardViewMode.NONE, - statusDisplayOptions.confirmReblogs() + statusDisplayOptions.confirmReblogs(), + statusDisplayOptions.quoteEnabled() ); } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 6bc6cc9bf..64e5fa420 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -754,6 +754,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { favouriteButton.setClickable(!isNotestock); if (quoteButton != null) { + if (!statusDisplayOptions.quoteEnabled()) { + quoteButton.setVisibility(View.GONE); + } + quoteButton.setOnClickListener(view -> { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java index 91ec25e2f..f8fb6c174 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java @@ -65,7 +65,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter { statusDisplayOptions.showBotOverlay(), statusDisplayOptions.useBlurhash(), statusDisplayOptions.cardViewMode(), - statusDisplayOptions.confirmReblogs() + statusDisplayOptions.confirmReblogs(), + statusDisplayOptions.quoteEnabled() ); } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index c72ddcd25..a111f7cc5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -160,7 +160,7 @@ class ComposeActivity : BaseActivity(), this.composeOptions = intent.getParcelableExtra(COMPOSE_OPTIONS_EXTRA) viewModel.setup(composeOptions) setupReplyViews(composeOptions?.replyingStatusAuthor) - setupQuoteView(composeOptions?.quoteUrl) + setupQuoteView(composeOptions?.quoteStatusAuthor) val tootText = composeOptions?.tootText if (!tootText.isNullOrEmpty()) { composeEditField.setText(tootText) @@ -301,11 +301,31 @@ class ComposeActivity : BaseActivity(), composeOptions?.replyingStatusContent?.let { composeReplyContentView.text = it } } - private fun setupQuoteView(quoteUrl: String?) { - if (quoteUrl != null) { + private fun setupQuoteView(quoteStatusAuthor: String?) { + if (quoteStatusAuthor != null) { composeQuoteView.show() - composeQuoteView.text = getString(R.string.quote_to, quoteUrl) + composeQuoteView.text = getString(R.string.quote_to, quoteStatusAuthor) + val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 } + + ThemeUtils.setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary) + composeQuoteView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) + + composeQuoteView.setOnClickListener { + TransitionManager.beginDelayedTransition(composeQuoteContentView.parent as ViewGroup) + + if (composeQuoteContentView.isVisible) { + composeQuoteContentView.hide() + composeQuoteView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) + } else { + composeQuoteContentView.show() + val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 } + + ThemeUtils.setDrawableTint(this, arrowUpIcon, android.R.attr.textColorTertiary) + composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowUpIcon, null) + } + } } + composeOptions?.quoteStatusContent?.let { composeQuoteContentView.text = it } } private fun setupContentWarningField(startingContentWarning: String?) { @@ -1083,7 +1103,8 @@ class ComposeActivity : BaseActivity(), var mentionedUsernames: Set? = null, var inReplyToId: String? = null, var quoteId: String? = null, - var quoteUrl: String? = null, + var quoteStatusAuthor: String? = null, + var quoteStatusContent: String? = null, var replyVisibility: Status.Visibility? = null, var visibility: Status.Visibility? = null, var contentWarning: String? = null, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt index 615346b56..cac5aace7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt @@ -61,7 +61,8 @@ class ComposeViewModel private var startingContentWarning: String = "" private var inReplyToId: String? = null private var quoteId: String? = null - private var quoteUrl: String? = null + private var quoteStatusAuthor: String? = null + private var quoteStatusContent: String? = null private var startingVisibility: Status.Visibility = Status.Visibility.UNKNOWN private var contentWarningStateChanged: Boolean = false @@ -268,14 +269,8 @@ class ComposeViewModel mediaDescriptions.add(item.description ?: "") } - var text = content - if (domain !in CAN_USE_QUOTE_ID && quoteId != null) { - text += "\n~~~~~~~~~~\n[$quoteUrl]" - quoteId = null - } - val tootToSend = TootToSend( - text, + content, spoilerText, statusVisibility.value!!.serverString(), mediaUris.isNotEmpty() && (markMediaAsSensitive.value!! || showContentWarning.value!!), @@ -394,7 +389,8 @@ class ComposeViewModel inReplyToId = composeOptions?.inReplyToId quoteId = composeOptions?.quoteId - quoteUrl = composeOptions?.quoteUrl + quoteStatusAuthor = composeOptions?.quoteStatusAuthor + quoteStatusContent = composeOptions?.quoteStatusContent val contentWarning = composeOptions?.contentWarning if (contentWarning != null) { @@ -485,7 +481,7 @@ const val DEFAULT_CHARACTER_LIMIT = 500 private const val DEFAULT_MAX_OPTION_COUNT = 4 private const val DEFAULT_MAX_OPTION_LENGTH = 25 -private val CAN_USE_QUOTE_ID = arrayOf("odakyu.app", "itabashi.0j0.jp", "biwakodon.com", "dtp-mstdn.jp", "nitiasa.com", +val CAN_USE_QUOTE_ID = arrayOf("odakyu.app", "itabashi.0j0.jp", "biwakodon.com", "dtp-mstdn.jp", "nitiasa.com", "comm.cx", "fedibird.com", "qoto.org", "kurage.cc", "m.eula.dev", "otogamer.me", "sgp.hostdon.ne.jp", "pomdon.work", "obapom.work") diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 4508a4de7..f87fb347d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator import com.keylesspalace.tusky.AccountActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.ViewTagActivity +import com.keylesspalace.tusky.components.compose.CAN_USE_QUOTE_ID import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory @@ -67,7 +68,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res showBotOverlay = preferences.getBoolean("showBotOverlay", true), useBlurhash = preferences.getBoolean("useBlurhash", true), cardViewMode = CardViewMode.NONE, - confirmReblogs = preferences.getBoolean("confirmReblogs", true) + confirmReblogs = preferences.getBoolean("confirmReblogs", true), + quoteEnabled = accountManager.activeAccount?.domain in CAN_USE_QUOTE_ID ) adapter = ConversationAdapter(statusDisplayOptions, this, ::onTopLoaded, viewModel::retry) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt index a318a95be..5df01503b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt @@ -34,6 +34,7 @@ import com.keylesspalace.tusky.AccountActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.ViewTagActivity +import com.keylesspalace.tusky.components.compose.CAN_USE_QUOTE_ID import com.keylesspalace.tusky.components.report.ReportViewModel import com.keylesspalace.tusky.components.report.Screen import com.keylesspalace.tusky.components.report.adapter.AdapterHandler @@ -118,7 +119,8 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler { showBotOverlay = false, useBlurhash = preferences.getBoolean("useBlurhash", true), cardViewMode = CardViewMode.NONE, - confirmReblogs = preferences.getBoolean("confirmReblogs", true) + confirmReblogs = preferences.getBoolean("confirmReblogs", true), + quoteEnabled = accountManager.activeAccount?.domain in CAN_USE_QUOTE_ID ) adapter = StatusesAdapter(statusDisplayOptions, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index 2acf2750f..279028ae9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.paging.PagedList +import com.keylesspalace.tusky.components.compose.CAN_USE_QUOTE_ID import com.keylesspalace.tusky.components.search.adapter.SearchNotestockRepository import com.keylesspalace.tusky.components.search.adapter.SearchRepository import com.keylesspalace.tusky.db.AccountEntity @@ -37,6 +38,7 @@ class SearchViewModel @Inject constructor( val mediaPreviewEnabled = activeAccount?.mediaPreviewEnabled ?: false val alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false val alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false + val quoteEnabled = activeAccount?.domain in CAN_USE_QUOTE_ID private val statusesRepository = SearchRepository>(mastodonApi) private val accountsRepository = SearchRepository(mastodonApi) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchNotestockFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchNotestockFragment.kt index e698d9bbe..a4c50e7a4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchNotestockFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchNotestockFragment.kt @@ -68,7 +68,8 @@ class SearchNotestockFragment : SearchFragment mentionedUsernames = new LinkedHashSet<>(); mentionedUsernames.add(actionableStatus.getAccount().getUsername()); @@ -187,12 +186,10 @@ public abstract class SFragment extends BaseFragment implements Injectable { mentionedUsernames.add(mention.getUsername()); } mentionedUsernames.remove(loggedInUsername); - if (status.getReblog() != null) { - url = status.getReblog().getUrl(); - } ComposeOptions composeOptions = new ComposeOptions(); composeOptions.setQuoteId(id); - composeOptions.setQuoteUrl(url); + composeOptions.setQuoteStatusAuthor(actionableStatus.getAccount().getLocalUsername()); + composeOptions.setQuoteStatusContent(actionableStatus.getContent().toString()); composeOptions.setReplyVisibility(visibility); composeOptions.setMentionedUsernames(mentionedUsernames); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index 3e701b438..03645d4c1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -65,6 +65,7 @@ import com.keylesspalace.tusky.appstore.StatusComposedEvent; import com.keylesspalace.tusky.appstore.StatusDeletedEvent; import com.keylesspalace.tusky.appstore.StreamUpdateEvent; import com.keylesspalace.tusky.appstore.UnfollowEvent; +import com.keylesspalace.tusky.components.compose.ComposeViewModelKt; import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.di.Injectable; @@ -101,7 +102,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.Objects; import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -251,7 +251,8 @@ public class TimelineFragment extends SFragment implements preferences.getBoolean("showCardsInTimelines", false) ? CardViewMode.INDENTED : CardViewMode.NONE, - preferences.getBoolean("confirmReblogs", true) + preferences.getBoolean("confirmReblogs", true), + Arrays.asList(ComposeViewModelKt.getCAN_USE_QUOTE_ID()).contains(accountManager.getActiveAccount().getDomain()) ); adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index 6d47e5aeb..71bc7d101 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -51,6 +51,7 @@ import com.keylesspalace.tusky.appstore.FavoriteEvent; import com.keylesspalace.tusky.appstore.ReblogEvent; import com.keylesspalace.tusky.appstore.StatusComposedEvent; import com.keylesspalace.tusky.appstore.StatusDeletedEvent; +import com.keylesspalace.tusky.components.compose.ComposeViewModelKt; import com.keylesspalace.tusky.di.Injectable; import com.keylesspalace.tusky.entity.Filter; import com.keylesspalace.tusky.entity.Poll; @@ -68,6 +69,7 @@ import com.keylesspalace.tusky.view.ConversationLineItemDecoration; import com.keylesspalace.tusky.viewdata.StatusViewData; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -136,7 +138,8 @@ public final class ViewThreadFragment extends SFragment implements preferences.getBoolean("showCardsInTimelines", false) ? CardViewMode.INDENTED : CardViewMode.NONE, - preferences.getBoolean("confirmReblogs", true) + preferences.getBoolean("confirmReblogs", true), + Arrays.asList(ComposeViewModelKt.getCAN_USE_QUOTE_ID()).contains(accountManager.getActiveAccount().getDomain()) ); adapter = new ThreadAdapter(statusDisplayOptions, this); } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt index eaaa5e19f..eca0e79c6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt @@ -14,5 +14,7 @@ data class StatusDisplayOptions( @get:JvmName("cardViewMode") val cardViewMode: CardViewMode, @get:JvmName("confirmReblogs") - val confirmReblogs: Boolean + val confirmReblogs: Boolean, + @get:JvmName("quoteEnabled") + val quoteEnabled: Boolean ) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml index 3afa7dc61..426950b43 100644 --- a/app/src/main/res/layout/activity_compose.xml +++ b/app/src/main/res/layout/activity_compose.xml @@ -92,13 +92,31 @@ android:id="@+id/composeQuoteView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="6dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" + android:layout_marginBottom="6dp" android:drawablePadding="6dp" android:textSize="?attr/status_text_small" android:textStyle="bold" - android:visibility="gone" /> + android:visibility="gone" + tools:text="Quote @username" + tools:visibility="visible" /> + +