Bookmarks (#1560)
* add bookmarks to timelines * add Bookmarks to main menu * cleanup * handle BookmarkEvent * fix tests * fix bookmark handling in NotificationsFragment * add bookmark accessibility actions
This commit is contained in:
parent
d6ec5ca8d3
commit
d9694df0c2
44 changed files with 1235 additions and 122 deletions
|
@ -1,76 +0,0 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.HasAndroidInjector;
|
||||
|
||||
public class FavouritesActivity extends BottomSheetActivity implements HasAndroidInjector {
|
||||
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_favourites);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar bar = getSupportActionBar();
|
||||
if (bar != null) {
|
||||
bar.setTitle(getString(R.string.title_favourites));
|
||||
bar.setDisplayHomeAsUpEnabled(true);
|
||||
bar.setDisplayShowHomeEnabled(true);
|
||||
}
|
||||
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
Fragment fragment = TimelineFragment.newInstance(TimelineFragment.Kind.FAVOURITES);
|
||||
fragmentTransaction.replace(R.id.fragment_container, fragment);
|
||||
fragmentTransaction.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<Object> androidInjector() {
|
||||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
}
|
|
@ -91,15 +91,16 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
||||
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
|
||||
private static final long DRAWER_ITEM_FAVOURITES = 1;
|
||||
private static final long DRAWER_ITEM_LISTS = 2;
|
||||
private static final long DRAWER_ITEM_SEARCH = 3;
|
||||
private static final long DRAWER_ITEM_SAVED_TOOT = 4;
|
||||
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 5;
|
||||
private static final long DRAWER_ITEM_SETTINGS = 6;
|
||||
private static final long DRAWER_ITEM_ABOUT = 7;
|
||||
private static final long DRAWER_ITEM_LOG_OUT = 8;
|
||||
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 9;
|
||||
private static final long DRAWER_ITEM_SCHEDULED_TOOT = 10;
|
||||
private static final long DRAWER_ITEM_BOOKMARKS = 2;
|
||||
private static final long DRAWER_ITEM_LISTS = 3;
|
||||
private static final long DRAWER_ITEM_SEARCH = 4;
|
||||
private static final long DRAWER_ITEM_SAVED_TOOT = 5;
|
||||
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 6;
|
||||
private static final long DRAWER_ITEM_SETTINGS = 7;
|
||||
private static final long DRAWER_ITEM_ABOUT = 8;
|
||||
private static final long DRAWER_ITEM_LOG_OUT = 9;
|
||||
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 10;
|
||||
private static final long DRAWER_ITEM_SCHEDULED_TOOT = 11;
|
||||
public static final String STATUS_URL = "statusUrl";
|
||||
|
||||
@Inject
|
||||
|
@ -144,8 +145,8 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
if (intent != null) {
|
||||
|
||||
/** there are two possibilities the accountId can be passed to MainActivity:
|
||||
- from our code as long 'account_id'
|
||||
- from share shortcuts as String 'android.intent.extra.shortcut.ID'
|
||||
- from our code as long 'account_id'
|
||||
- from share shortcuts as String 'android.intent.extra.shortcut.ID'
|
||||
*/
|
||||
long accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1);
|
||||
if(accountId == -1) {
|
||||
|
@ -387,9 +388,10 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
}
|
||||
});
|
||||
|
||||
List<IDrawerItem> listItems = new ArrayList<>(10);
|
||||
List<IDrawerItem> listItems = new ArrayList<>(11);
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BOOKMARKS).withName(R.string.action_view_bookmarks).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_bookmark));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
|
||||
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(R.drawable.ic_notebook).withIconTintingEnabled(true));
|
||||
|
@ -415,7 +417,10 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
Intent intent = new Intent(MainActivity.this, EditProfileActivity.class);
|
||||
startActivityWithSlideInAnimation(intent);
|
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_FAVOURITES) {
|
||||
Intent intent = new Intent(MainActivity.this, FavouritesActivity.class);
|
||||
Intent intent = StatusListActivity.newFavouritesIntent(MainActivity.this);
|
||||
startActivityWithSlideInAnimation(intent);
|
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_BOOKMARKS) {
|
||||
Intent intent = StatusListActivity.newBookmarksIntent(MainActivity.this);
|
||||
startActivityWithSlideInAnimation(intent);
|
||||
} else if (drawerItemIdentifier == DRAWER_ITEM_SEARCH) {
|
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(this));
|
||||
|
@ -591,7 +596,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
|||
.withName(R.string.action_view_follow_requests)
|
||||
.withSelectable(false)
|
||||
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
||||
drawer.addItemAtPosition(followRequestsItem, 3);
|
||||
drawer.addItemAtPosition(followRequestsItem, 4);
|
||||
} else if (!me.getLocked()) {
|
||||
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* Copyright 2019 Tusky Contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <https://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.commit
|
||||
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment.Kind
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import kotlinx.android.extensions.CacheImplementation
|
||||
import kotlinx.android.extensions.ContainerOptions
|
||||
import kotlinx.android.synthetic.main.toolbar_basic.*
|
||||
|
||||
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||
|
||||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
private val kind: Kind
|
||||
get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
|
||||
|
||||
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_statuslist)
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val title = if(kind == Kind.FAVOURITES) {
|
||||
R.string.title_favourites
|
||||
} else {
|
||||
R.string.title_bookmarks
|
||||
}
|
||||
|
||||
supportActionBar?.run {
|
||||
setTitle(title)
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
supportFragmentManager.commit {
|
||||
val fragment = TimelineFragment.newInstance(kind)
|
||||
replace(R.id.fragment_container, fragment)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home){
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun androidInjector() = dispatchingAndroidInjector
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_KIND = "kind"
|
||||
|
||||
@JvmStatic
|
||||
fun newFavouritesIntent(context: Context) =
|
||||
Intent(context, StatusListActivity::class.java).apply {
|
||||
putExtra(EXTRA_KIND, Kind.FAVOURITES.name)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newBookmarksIntent(context: Context) =
|
||||
Intent(context, StatusListActivity::class.java).apply {
|
||||
putExtra(EXTRA_KIND, Kind.BOOKMARKS.name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -71,7 +71,8 @@ public class TuskyApplication extends Application implements HasAndroidInjector
|
|||
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
|
||||
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
||||
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19)
|
||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
||||
AppDatabase.MIGRATION_19_20)
|
||||
.build();
|
||||
accountManager = new AccountManager(appDatabase);
|
||||
serviceLocator = new ServiceLocator() {
|
||||
|
|
|
@ -64,6 +64,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
private ImageButton replyButton;
|
||||
private SparkButton reblogButton;
|
||||
private SparkButton favouriteButton;
|
||||
private SparkButton bookmarkButton;
|
||||
private ImageButton moreButton;
|
||||
protected MediaPreviewImageView[] mediaPreviews;
|
||||
private ImageView[] mediaOverlays;
|
||||
|
@ -107,6 +108,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
replyButton = itemView.findViewById(R.id.status_reply);
|
||||
reblogButton = itemView.findViewById(R.id.status_inset);
|
||||
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
||||
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
||||
moreButton = itemView.findViewById(R.id.status_more);
|
||||
|
||||
mediaPreviews = new MediaPreviewImageView[]{
|
||||
|
@ -348,6 +350,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
favouriteButton.setChecked(favourited);
|
||||
}
|
||||
|
||||
protected void setBookmarked(boolean bookmarked) {
|
||||
bookmarkButton.setChecked(bookmarked);
|
||||
}
|
||||
|
||||
private void loadImage(MediaPreviewImageView imageView, String previewUrl, MetaData meta) {
|
||||
if (TextUtils.isEmpty(previewUrl)) {
|
||||
Glide.with(imageView)
|
||||
|
@ -582,6 +588,27 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
public void onEventAnimationStart(ImageView button, boolean buttonState) {
|
||||
}
|
||||
});
|
||||
|
||||
bookmarkButton.setEventListener(new SparkEventListener() {
|
||||
@Override
|
||||
public void onEvent(ImageView button, boolean buttonState) {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onBookmark(buttonState, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEventAnimationEnd(ImageView button, boolean buttonState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEventAnimationStart(ImageView button, boolean buttonState) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
moreButton.setOnClickListener(v -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
|
@ -621,6 +648,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), showBotOverlay, animateAvatar);
|
||||
setReblogged(status.isReblogged());
|
||||
setFavourited(status.isFavourited());
|
||||
setBookmarked(status.isBookmarked());
|
||||
List<Attachment> attachments = status.getAttachments();
|
||||
boolean sensitive = status.isSensitive();
|
||||
if (mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
||||
|
@ -690,6 +718,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
status.getNickname(),
|
||||
status.isReblogged() ? context.getString(R.string.description_status_reblogged) : "",
|
||||
status.isFavourited() ? context.getString(R.string.description_status_favourited) : "",
|
||||
status.isBookmarked() ? context.getString(R.string.description_status_bookmarked) : "",
|
||||
getMediaDescription(context, status),
|
||||
getVisibilityDescription(context, status.getVisibility()),
|
||||
getFavsText(context, status.getFavouritesCount()),
|
||||
|
|
|
@ -26,6 +26,8 @@ class CacheUpdater @Inject constructor(
|
|||
timelineDao.setFavourited(accountId, event.statusId, event.favourite)
|
||||
is ReblogEvent ->
|
||||
timelineDao.setReblogged(accountId, event.statusId, event.reblog)
|
||||
is BookmarkEvent ->
|
||||
timelineDao.setBookmarked(accountId, event.statusId, event.bookmark )
|
||||
is UnfollowEvent ->
|
||||
timelineDao.removeAllByUser(accountId, event.accountId)
|
||||
is StatusDeletedEvent ->
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.keylesspalace.tusky.entity.Status
|
|||
|
||||
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
||||
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
||||
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
||||
data class UnfollowEvent(val accountId: String) : Dispatchable
|
||||
data class BlockEvent(val accountId: String) : Dispatchable
|
||||
data class MuteEvent(val accountId: String) : Dispatchable
|
||||
|
|
|
@ -69,6 +69,7 @@ data class ConversationStatusEntity(
|
|||
val emojis: List<Emoji>,
|
||||
val favouritesCount: Int,
|
||||
val favourited: Boolean,
|
||||
val bookmarked: Boolean,
|
||||
val sensitive: Boolean,
|
||||
val spoilerText: String,
|
||||
val attachments: ArrayList<Attachment>,
|
||||
|
@ -148,6 +149,7 @@ data class ConversationStatusEntity(
|
|||
favouritesCount = favouritesCount,
|
||||
reblogged = false,
|
||||
favourited = favourited,
|
||||
bookmarked = bookmarked,
|
||||
sensitive= sensitive,
|
||||
spoilerText = spoilerText,
|
||||
visibility = Status.Visibility.DIRECT,
|
||||
|
@ -172,7 +174,7 @@ fun Account.toEntity() =
|
|||
fun Status.toEntity() =
|
||||
ConversationStatusEntity(
|
||||
id, url, inReplyToId, inReplyToAccountId, account.toEntity(), content,
|
||||
createdAt, emojis, favouritesCount, favourited, sensitive,
|
||||
createdAt, emojis, favouritesCount, favourited, bookmarked, sensitive,
|
||||
spoilerText, attachments, mentions,
|
||||
false,
|
||||
false,
|
||||
|
|
|
@ -83,6 +83,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
|||
setCreatedAt(status.getCreatedAt());
|
||||
setIsReply(status.getInReplyToId() != null);
|
||||
setFavourited(status.getFavourited());
|
||||
setBookmarked(status.getBookmarked());
|
||||
List<Attachment> attachments = status.getAttachments();
|
||||
boolean sensitive = status.getSensitive();
|
||||
if(mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
|
||||
|
|
|
@ -117,6 +117,10 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
viewModel.favourite(favourite, position)
|
||||
}
|
||||
|
||||
override fun onBookmark(favourite: Boolean, position: Int) {
|
||||
viewModel.bookmark(favourite, position)
|
||||
}
|
||||
|
||||
override fun onMore(view: View, position: Int) {
|
||||
viewModel.conversations.value?.getOrNull(position)?.lastStatus?.let {
|
||||
more(it.toStatus(), view, position)
|
||||
|
|
|
@ -66,6 +66,24 @@ class ConversationsViewModel @Inject constructor(
|
|||
|
||||
}
|
||||
|
||||
fun bookmark(bookmark: Boolean, position: Int) {
|
||||
conversations.value?.getOrNull(position)?.let { conversation ->
|
||||
timelineCases.bookmark(conversation.lastStatus.toStatus(), bookmark)
|
||||
.flatMap {
|
||||
val newConversation = conversation.copy(
|
||||
lastStatus = conversation.lastStatus.copy(bookmarked = bookmark)
|
||||
)
|
||||
|
||||
database.conversationDao().insert(newConversation)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.doOnError { t -> Log.w("ConversationViewModel", "Failed to bookmark conversation", t) }
|
||||
.subscribe()
|
||||
.addTo(disposables)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun voteInPoll(position: Int, choices: MutableList<Int>) {
|
||||
conversations.value?.getOrNull(position)?.let { conversation ->
|
||||
timelineCases.voteInPoll(conversation.lastStatus.toStatus(), choices)
|
||||
|
|
|
@ -187,6 +187,18 @@ class SearchViewModel @Inject constructor(
|
|||
.subscribe())
|
||||
}
|
||||
|
||||
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
|
||||
val idx = loadedStatuses.indexOf(status)
|
||||
if (idx >= 0) {
|
||||
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setFavourited(isBookmarked).createStatusViewData())
|
||||
loadedStatuses[idx] = newPair
|
||||
repoResultStatus.value?.refresh?.invoke()
|
||||
}
|
||||
disposables.add(timelineCases.favourite(status.first, isBookmarked)
|
||||
.onErrorReturnItem(status.first)
|
||||
.subscribe())
|
||||
}
|
||||
|
||||
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
||||
return accountManager.getAllAccountsOrderedByActive()
|
||||
}
|
||||
|
|
|
@ -94,6 +94,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
|||
}
|
||||
}
|
||||
|
||||
override fun onBookmark(bookmark: Boolean, position: Int) {
|
||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status ->
|
||||
viewModel.bookmark(status, bookmark)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMore(view: View, position: Int) {
|
||||
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let {
|
||||
more(it, view, position)
|
||||
|
|
|
@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
|
|||
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
||||
TimelineAccountEntity.class, ConversationEntity.class
|
||||
}, version = 19)
|
||||
}, version = 20)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TootDao tootDao();
|
||||
|
@ -310,4 +310,12 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_19_20 = new Migration(19, 20) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `bookmarked` INTEGER NOT NULL DEFAULT 0");
|
||||
database.execSQL("ALTER TABLE `ConversationEntity` ADD COLUMN `s_bookmarked` INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -24,7 +24,7 @@ abstract class TimelineDao {
|
|||
@Query("""
|
||||
SELECT s.serverId, s.url, s.timelineUserId,
|
||||
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt,
|
||||
s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.sensitive,
|
||||
s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
||||
s.spoilerText, s.visibility, s.mentions, s.application, s.reblogServerId,s.reblogAccountId,
|
||||
s.content, s.attachments, s.poll,
|
||||
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
|
||||
|
@ -77,6 +77,9 @@ AND
|
|||
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||
abstract fun setFavourited(accountId: Long, statusId: String, favourited: Boolean)
|
||||
|
||||
@Query("""UPDATE TimelineStatusEntity SET bookmarked = :bookmarked
|
||||
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||
abstract fun setBookmarked(accountId: Long, statusId: String, bookmarked: Boolean)
|
||||
|
||||
@Query("""UPDATE TimelineStatusEntity SET reblogged = :reblogged
|
||||
WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""")
|
||||
|
|
|
@ -41,6 +41,7 @@ data class TimelineStatusEntity(
|
|||
val reblogsCount: Int,
|
||||
val favouritesCount: Int,
|
||||
val reblogged: Boolean,
|
||||
val bookmarked: Boolean,
|
||||
val favourited: Boolean,
|
||||
val sensitive: Boolean,
|
||||
val spoilerText: String?,
|
||||
|
|
|
@ -60,10 +60,10 @@ abstract class ActivitiesModule {
|
|||
abstract fun contributesViewThreadActivity(): ViewThreadActivity
|
||||
|
||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||
abstract fun contributesFavouritesActivity(): FavouritesActivity
|
||||
abstract fun contributesStatusListActivity(): StatusListActivity
|
||||
|
||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||
abstract fun contribtutesSearchAvtivity(): SearchActivity
|
||||
abstract fun contributesSearchAvtivity(): SearchActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesAboutActivity(): AboutActivity
|
||||
|
|
|
@ -35,6 +35,7 @@ data class Status(
|
|||
@SerializedName("favourites_count") val favouritesCount: Int,
|
||||
var reblogged: Boolean,
|
||||
var favourited: Boolean,
|
||||
var bookmarked: Boolean,
|
||||
var sensitive: Boolean,
|
||||
@SerializedName("spoiler_text") val spoilerText: String,
|
||||
val visibility: Visibility,
|
||||
|
|
|
@ -55,6 +55,7 @@ import com.keylesspalace.tusky.R;
|
|||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||
import com.keylesspalace.tusky.appstore.EventHub;
|
||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
||||
|
@ -309,6 +310,16 @@ public class NotificationsFragment extends SFragment implements
|
|||
event.getFavourite());
|
||||
}
|
||||
|
||||
private void handleBookmarkEvent(BookmarkEvent event) {
|
||||
Pair<Integer, Notification> posAndNotification =
|
||||
findReplyPosition(event.getStatusId());
|
||||
if (posAndNotification == null) return;
|
||||
//noinspection ConstantConditions
|
||||
setBookmarkForStatus(posAndNotification.first,
|
||||
posAndNotification.second.getStatus(),
|
||||
event.getBookmark());
|
||||
}
|
||||
|
||||
private void handleReblogEvent(ReblogEvent event) {
|
||||
Pair<Integer, Notification> posAndNotification = findReplyPosition(event.getStatusId());
|
||||
if (posAndNotification == null) return;
|
||||
|
@ -365,6 +376,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
.subscribe(event -> {
|
||||
if (event instanceof FavoriteEvent) {
|
||||
handleFavEvent((FavoriteEvent) event);
|
||||
} else if (event instanceof BookmarkEvent) {
|
||||
handleBookmarkEvent((BookmarkEvent) event);
|
||||
} else if (event instanceof ReblogEvent) {
|
||||
handleReblogEvent((ReblogEvent) event);
|
||||
} else if (event instanceof BlockEvent) {
|
||||
|
@ -463,6 +476,41 @@ public class NotificationsFragment extends SFragment implements
|
|||
updateAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBookmark(final boolean bookmark, final int position) {
|
||||
final Notification notification = notifications.get(position).asRight();
|
||||
final Status status = notification.getStatus();
|
||||
|
||||
timelineCases.bookmark(status, bookmark)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> setBookmarkForStatus(position, status, bookmark),
|
||||
(t) -> Log.d(getClass().getSimpleName(),
|
||||
"Failed to bookmark status: " + status.getId(), t)
|
||||
);
|
||||
}
|
||||
|
||||
private void setBookmarkForStatus(int position, Status status, boolean bookmark) {
|
||||
status.setBookmarked(bookmark);
|
||||
|
||||
if (status.getReblog() != null) {
|
||||
status.getReblog().setBookmarked(bookmark);
|
||||
}
|
||||
|
||||
NotificationViewData.Concrete viewdata = (NotificationViewData.Concrete) notifications.getPairedItem(position);
|
||||
|
||||
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder(viewdata.getStatusViewData());
|
||||
viewDataBuilder.setBookmarked(bookmark);
|
||||
|
||||
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
|
||||
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
|
||||
viewDataBuilder.createStatusViewData(), viewdata.isExpanded());
|
||||
|
||||
notifications.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
||||
final Notification notification = notifications.get(position).asRight();
|
||||
final Status status = notification.getStatus();
|
||||
|
|
|
@ -49,6 +49,7 @@ import com.keylesspalace.tusky.R;
|
|||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||
import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
|
||||
import com.keylesspalace.tusky.appstore.EventHub;
|
||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||
|
@ -128,7 +129,8 @@ public class TimelineFragment extends SFragment implements
|
|||
USER_PINNED,
|
||||
USER_WITH_REPLIES,
|
||||
FAVOURITES,
|
||||
LIST
|
||||
LIST,
|
||||
BOOKMARKS
|
||||
}
|
||||
|
||||
private enum FetchEnd {
|
||||
|
@ -492,6 +494,9 @@ public class TimelineFragment extends SFragment implements
|
|||
} else if (event instanceof ReblogEvent) {
|
||||
ReblogEvent reblogEvent = (ReblogEvent) event;
|
||||
handleReblogEvent(reblogEvent);
|
||||
} else if (event instanceof BookmarkEvent) {
|
||||
BookmarkEvent bookmarkEvent = (BookmarkEvent) event;
|
||||
handleBookmarkEvent(bookmarkEvent);
|
||||
} else if (event instanceof UnfollowEvent) {
|
||||
if (kind == Kind.HOME) {
|
||||
String id = ((UnfollowEvent) event).getAccountId();
|
||||
|
@ -630,6 +635,38 @@ public class TimelineFragment extends SFragment implements
|
|||
updateAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBookmark(final boolean bookmark, final int position) {
|
||||
final Status status = statuses.get(position).asRight();
|
||||
|
||||
timelineCases.bookmark(status, bookmark)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
.subscribe(
|
||||
(newStatus) -> setBookmarkForStatus(position, newStatus, bookmark),
|
||||
(err) -> Log.d(TAG, "Failed to favourite status " + status.getId(), err)
|
||||
);
|
||||
}
|
||||
|
||||
private void setBookmarkForStatus(int position, Status status, boolean bookmark) {
|
||||
status.setBookmarked(bookmark);
|
||||
|
||||
if (status.getReblog() != null) {
|
||||
status.getReblog().setBookmarked(bookmark);
|
||||
}
|
||||
|
||||
Pair<StatusViewData.Concrete, Integer> actual =
|
||||
findStatusAndPosition(position, status);
|
||||
if (actual == null) return;
|
||||
|
||||
StatusViewData newViewData = new StatusViewData
|
||||
.Builder(actual.first)
|
||||
.setBookmarked(bookmark)
|
||||
.createStatusViewData();
|
||||
statuses.setPairedItem(actual.second, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
public void onVoteInPoll(int position, @NonNull List<Integer> choices) {
|
||||
|
||||
final Status status = statuses.get(position).asRight();
|
||||
|
@ -917,7 +954,7 @@ public class TimelineFragment extends SFragment implements
|
|||
}
|
||||
|
||||
private boolean actionButtonPresent() {
|
||||
return kind != Kind.TAG && kind != Kind.FAVOURITES &&
|
||||
return kind != Kind.TAG && kind != Kind.FAVOURITES && kind != Kind.BOOKMARKS &&
|
||||
getActivity() instanceof ActionButtonActivity;
|
||||
}
|
||||
|
||||
|
@ -950,6 +987,8 @@ public class TimelineFragment extends SFragment implements
|
|||
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, null, null, null);
|
||||
case FAVOURITES:
|
||||
return api.favourites(fromId, uptoId, LOAD_AT_ONCE);
|
||||
case BOOKMARKS:
|
||||
return api.bookmarks(fromId, uptoId, LOAD_AT_ONCE);
|
||||
case LIST:
|
||||
return api.listTimeline(tagOrId, fromId, uptoId, LOAD_AT_ONCE);
|
||||
}
|
||||
|
@ -1095,11 +1134,8 @@ public class TimelineFragment extends SFragment implements
|
|||
}
|
||||
|
||||
private void updateBottomLoadingState(FetchEnd fetchEnd) {
|
||||
switch (fetchEnd) {
|
||||
case BOTTOM: {
|
||||
bottomLoading = false;
|
||||
break;
|
||||
}
|
||||
if (fetchEnd == FetchEnd.BOTTOM) {
|
||||
bottomLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1223,8 +1259,8 @@ public class TimelineFragment extends SFragment implements
|
|||
private final Function1<Status, Either<Placeholder, Status>> statusLifter =
|
||||
Either.Right::new;
|
||||
|
||||
private @Nullable
|
||||
Pair<StatusViewData.Concrete, Integer>
|
||||
@Nullable
|
||||
private Pair<StatusViewData.Concrete, Integer>
|
||||
findStatusAndPosition(int position, Status status) {
|
||||
StatusViewData.Concrete statusToUpdate;
|
||||
int positionToUpdate;
|
||||
|
@ -1260,6 +1296,13 @@ public class TimelineFragment extends SFragment implements
|
|||
setFavouriteForStatus(pos, status, favEvent.getFavourite());
|
||||
}
|
||||
|
||||
private void handleBookmarkEvent(@NonNull BookmarkEvent bookmarkEvent) {
|
||||
int pos = findStatusOrReblogPositionById(bookmarkEvent.getStatusId());
|
||||
if (pos < 0) return;
|
||||
Status status = statuses.get(pos).asRight();
|
||||
setBookmarkForStatus(pos, status, bookmarkEvent.getBookmark());
|
||||
}
|
||||
|
||||
private void handleStatusComposeEvent(@NonNull Status status) {
|
||||
switch (kind) {
|
||||
case HOME:
|
||||
|
|
|
@ -45,6 +45,7 @@ import com.keylesspalace.tusky.R;
|
|||
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||
import com.keylesspalace.tusky.appstore.EventHub;
|
||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||
|
@ -186,6 +187,8 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
handleFavEvent((FavoriteEvent) event);
|
||||
} else if (event instanceof ReblogEvent) {
|
||||
handleReblogEvent((ReblogEvent) event);
|
||||
} else if (event instanceof BookmarkEvent) {
|
||||
handleBookmarkEvent((BookmarkEvent) event);
|
||||
} else if (event instanceof BlockEvent) {
|
||||
removeAllByAccountId(((BlockEvent) event).getAccountId());
|
||||
} else if (event instanceof StatusComposedEvent) {
|
||||
|
@ -239,7 +242,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> updateStatus(position, newStatus),
|
||||
(t) -> Log.d(getClass().getSimpleName(),
|
||||
(t) -> Log.d(TAG,
|
||||
"Failed to reblog status: " + status.getId(), t)
|
||||
);
|
||||
}
|
||||
|
@ -253,11 +256,25 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> updateStatus(position, newStatus),
|
||||
(t) -> Log.d(getClass().getSimpleName(),
|
||||
(t) -> Log.d(TAG,
|
||||
"Failed to favourite status: " + status.getId(), t)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBookmark(final boolean bookmark, final int position) {
|
||||
final Status status = statuses.get(position);
|
||||
|
||||
timelineCases.bookmark(statuses.get(position), bookmark)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> updateStatus(position, newStatus),
|
||||
(t) -> Log.d(TAG,
|
||||
"Failed to bookmark status: " + status.getId(), t)
|
||||
);
|
||||
}
|
||||
|
||||
private void updateStatus(int position, Status status) {
|
||||
if (position >= 0 && position < statuses.size()) {
|
||||
|
||||
|
@ -267,6 +284,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
.setReblogged(actionableStatus.getReblogged())
|
||||
.setReblogsCount(actionableStatus.getReblogsCount())
|
||||
.setFavourited(actionableStatus.getFavourited())
|
||||
.setBookmarked(actionableStatus.getBookmarked())
|
||||
.setFavouritesCount(actionableStatus.getFavouritesCount())
|
||||
.createStatusViewData();
|
||||
statuses.setPairedItem(position, viewData);
|
||||
|
@ -621,6 +639,28 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
adapter.setItem(posAndStatus.first, newViewData, true);
|
||||
}
|
||||
|
||||
private void handleBookmarkEvent(BookmarkEvent event) {
|
||||
Pair<Integer, Status> posAndStatus = findStatusAndPos(event.getStatusId());
|
||||
if (posAndStatus == null) return;
|
||||
|
||||
boolean bookmark = event.getBookmark();
|
||||
posAndStatus.second.setBookmarked(bookmark);
|
||||
|
||||
if (posAndStatus.second.getReblog() != null) {
|
||||
posAndStatus.second.getReblog().setBookmarked(bookmark);
|
||||
}
|
||||
|
||||
StatusViewData.Concrete viewdata = statuses.getPairedItem(posAndStatus.first);
|
||||
|
||||
StatusViewData.Builder viewDataBuilder = new StatusViewData.Builder((viewdata));
|
||||
viewDataBuilder.setBookmarked(bookmark);
|
||||
|
||||
StatusViewData.Concrete newViewData = viewDataBuilder.createStatusViewData();
|
||||
|
||||
statuses.setPairedItem(posAndStatus.first, newViewData);
|
||||
adapter.setItem(posAndStatus.first, newViewData, true);
|
||||
}
|
||||
|
||||
private void handleStatusComposedEvent(StatusComposedEvent event) {
|
||||
Status eventStatus = event.getStatus();
|
||||
if (eventStatus.getInReplyToId() == null) return;
|
||||
|
|
|
@ -26,6 +26,7 @@ public interface StatusActionListener extends LinkListener {
|
|||
void onReply(int position);
|
||||
void onReblog(final boolean reblog, final int position);
|
||||
void onFavourite(final boolean favourite, final int position);
|
||||
void onBookmark(final boolean bookmark, final int position);
|
||||
void onMore(@NonNull View view, final int position);
|
||||
void onViewMedia(int position, int attachmentIndex, @Nullable View view);
|
||||
void onViewThread(int position);
|
||||
|
|
|
@ -180,6 +180,16 @@ interface MastodonApi {
|
|||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
@POST("api/v1/statuses/{id}/bookmark")
|
||||
fun bookmarkStatus(
|
||||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
@POST("api/v1/statuses/{id}/unbookmark")
|
||||
fun unbookmarkStatus(
|
||||
@Path("id") statusId: String
|
||||
): Single<Status>
|
||||
|
||||
@POST("api/v1/statuses/{id}/pin")
|
||||
fun pinStatus(
|
||||
@Path("id") statusId: String
|
||||
|
@ -343,6 +353,13 @@ interface MastodonApi {
|
|||
@Query("limit") limit: Int?
|
||||
): Call<List<Status>>
|
||||
|
||||
@GET("api/v1/bookmarks")
|
||||
fun bookmarks(
|
||||
@Query("max_id") maxId: String?,
|
||||
@Query("since_id") sinceId: String?,
|
||||
@Query("limit") limit: Int?
|
||||
): Call<List<Status>>
|
||||
|
||||
@GET("api/v1/follow_requests")
|
||||
fun followRequests(
|
||||
@Query("max_id") maxId: String?
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.lang.IllegalStateException
|
|||
interface TimelineCases {
|
||||
fun reblog(status: Status, reblog: Boolean): Single<Status>
|
||||
fun favourite(status: Status, favourite: Boolean): Single<Status>
|
||||
fun bookmark(status: Status, bookmark: Boolean): Single<Status>
|
||||
fun mute(id: String)
|
||||
fun block(id: String)
|
||||
fun delete(id: String): Single<DeletedStatus>
|
||||
|
@ -80,6 +81,19 @@ class TimelineCasesImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun bookmark(status: Status, bookmark: Boolean): Single<Status> {
|
||||
val id = status.actionableId
|
||||
|
||||
val call = if (bookmark) {
|
||||
mastodonApi.bookmarkStatus(id)
|
||||
} else {
|
||||
mastodonApi.unbookmarkStatus(id)
|
||||
}
|
||||
return call.doAfterSuccess {
|
||||
eventHub.dispatch(BookmarkEvent(status.id, bookmark))
|
||||
}
|
||||
}
|
||||
|
||||
override fun mute(id: String) {
|
||||
val call = mastodonApi.muteAccount(id)
|
||||
call.enqueue(object : Callback<Relationship> {
|
||||
|
|
|
@ -221,6 +221,7 @@ class TimelineRepositoryImpl(
|
|||
favouritesCount = status.favouritesCount,
|
||||
reblogged = status.reblogged,
|
||||
favourited = status.favourited,
|
||||
bookmarked = status.bookmarked,
|
||||
sensitive = status.sensitive,
|
||||
spoilerText = status.spoilerText!!,
|
||||
visibility = status.visibility!!,
|
||||
|
@ -247,6 +248,7 @@ class TimelineRepositoryImpl(
|
|||
favouritesCount = 0,
|
||||
reblogged = false,
|
||||
favourited = false,
|
||||
bookmarked = false,
|
||||
sensitive = false,
|
||||
spoilerText = "",
|
||||
visibility = status.visibility!!,
|
||||
|
@ -272,6 +274,7 @@ class TimelineRepositoryImpl(
|
|||
favouritesCount = status.favouritesCount,
|
||||
reblogged = status.reblogged,
|
||||
favourited = status.favourited,
|
||||
bookmarked = status.bookmarked,
|
||||
sensitive = status.sensitive,
|
||||
spoilerText = status.spoilerText!!,
|
||||
visibility = status.visibility!!,
|
||||
|
@ -341,6 +344,7 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
|
|||
favouritesCount = 0,
|
||||
reblogged = false,
|
||||
favourited = false,
|
||||
bookmarked = false,
|
||||
sensitive = false,
|
||||
spoilerText = null,
|
||||
visibility = null,
|
||||
|
@ -371,6 +375,7 @@ fun Status.toEntity(timelineUserId: Long,
|
|||
favouritesCount = actionable.favouritesCount,
|
||||
reblogged = actionable.reblogged,
|
||||
favourited = actionable.favourited,
|
||||
bookmarked = actionable.bookmarked,
|
||||
sensitive = actionable.sensitive,
|
||||
spoilerText = actionable.spoilerText,
|
||||
visibility = actionable.visibility,
|
||||
|
|
|
@ -56,6 +56,7 @@ class ListStatusAccessibilityDelegate(
|
|||
info.addAction(if (status.isReblogged) unreblogAction else reblogAction)
|
||||
}
|
||||
info.addAction(if (status.isFavourited) unfavouriteAction else favouriteAction)
|
||||
info.addAction(if (status.isBookmarked) unbookmarkAction else bookmarkAction)
|
||||
|
||||
val mediaActions = intArrayOf(
|
||||
R.id.action_open_media_1,
|
||||
|
@ -95,6 +96,8 @@ class ListStatusAccessibilityDelegate(
|
|||
}
|
||||
R.id.action_favourite -> statusActionListener.onFavourite(true, pos)
|
||||
R.id.action_unfavourite -> statusActionListener.onFavourite(false, pos)
|
||||
R.id.action_bookmark -> statusActionListener.onBookmark(true, pos)
|
||||
R.id.action_unbookmark -> statusActionListener.onBookmark(false, pos)
|
||||
R.id.action_reblog -> statusActionListener.onReblog(true, pos)
|
||||
R.id.action_unreblog -> statusActionListener.onReblog(false, pos)
|
||||
R.id.action_open_profile -> {
|
||||
|
@ -272,6 +275,14 @@ class ListStatusAccessibilityDelegate(
|
|||
R.id.action_favourite,
|
||||
context.getString(R.string.action_favourite))
|
||||
|
||||
private val bookmarkAction = AccessibilityActionCompat(
|
||||
R.id.action_bookmark,
|
||||
context.getString(R.string.action_bookmark))
|
||||
|
||||
private val unbookmarkAction = AccessibilityActionCompat(
|
||||
R.id.action_unbookmark,
|
||||
context.getString(R.string.action_bookmark))
|
||||
|
||||
private val openProfileAction = AccessibilityActionCompat(
|
||||
R.id.action_open_profile,
|
||||
context.getString(R.string.action_view_profile))
|
||||
|
|
|
@ -42,6 +42,7 @@ public final class ViewDataUtils {
|
|||
.setFavouritesCount(visibleStatus.getFavouritesCount())
|
||||
.setInReplyToId(visibleStatus.getInReplyToId())
|
||||
.setFavourited(visibleStatus.getFavourited())
|
||||
.setBookmarked(visibleStatus.getBookmarked())
|
||||
.setReblogged(visibleStatus.getReblogged())
|
||||
.setIsExpanded(alwaysOpenSpoiler)
|
||||
.setIsShowingSensitiveContent(false)
|
||||
|
|
|
@ -42,8 +42,7 @@ import java.util.Objects;
|
|||
|
||||
public abstract class StatusViewData {
|
||||
|
||||
private StatusViewData() {
|
||||
}
|
||||
private StatusViewData() { }
|
||||
|
||||
public abstract long getViewDataId();
|
||||
|
||||
|
@ -57,6 +56,7 @@ public abstract class StatusViewData {
|
|||
private final Spanned content;
|
||||
final boolean reblogged;
|
||||
final boolean favourited;
|
||||
final boolean bookmarked;
|
||||
@Nullable
|
||||
private final String spoilerText;
|
||||
private final Status.Visibility visibility;
|
||||
|
@ -92,7 +92,7 @@ public abstract class StatusViewData {
|
|||
private final PollViewData poll;
|
||||
private final boolean isBot;
|
||||
|
||||
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited,
|
||||
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked,
|
||||
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
||||
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
||||
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
||||
|
@ -114,6 +114,7 @@ public abstract class StatusViewData {
|
|||
}
|
||||
this.reblogged = reblogged;
|
||||
this.favourited = favourited;
|
||||
this.bookmarked = bookmarked;
|
||||
this.visibility = visibility;
|
||||
this.attachments = attachments;
|
||||
this.rebloggedByUsername = rebloggedByUsername;
|
||||
|
@ -156,6 +157,10 @@ public abstract class StatusViewData {
|
|||
return favourited;
|
||||
}
|
||||
|
||||
public boolean isBookmarked() {
|
||||
return bookmarked;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSpoilerText() {
|
||||
return spoilerText;
|
||||
|
@ -288,6 +293,7 @@ public abstract class StatusViewData {
|
|||
Concrete concrete = (Concrete) o;
|
||||
return reblogged == concrete.reblogged &&
|
||||
favourited == concrete.favourited &&
|
||||
bookmarked == concrete.bookmarked &&
|
||||
isSensitive == concrete.isSensitive &&
|
||||
isExpanded == concrete.isExpanded &&
|
||||
isShowingContent == concrete.isShowingContent &&
|
||||
|
@ -394,6 +400,7 @@ public abstract class StatusViewData {
|
|||
private Spanned content;
|
||||
private boolean reblogged;
|
||||
private boolean favourited;
|
||||
private boolean bookmarked;
|
||||
private String spoilerText;
|
||||
private Status.Visibility visibility;
|
||||
private List<Attachment> attachments;
|
||||
|
@ -429,6 +436,7 @@ public abstract class StatusViewData {
|
|||
content = viewData.content;
|
||||
reblogged = viewData.reblogged;
|
||||
favourited = viewData.favourited;
|
||||
bookmarked = viewData.bookmarked;
|
||||
spoilerText = viewData.spoilerText;
|
||||
visibility = viewData.visibility;
|
||||
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
|
||||
|
@ -477,6 +485,11 @@ public abstract class StatusViewData {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setBookmarked(boolean bookmarked) {
|
||||
this.bookmarked = bookmarked;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSpoilerText(String spoilerText) {
|
||||
this.spoilerText = spoilerText;
|
||||
return this;
|
||||
|
@ -626,8 +639,8 @@ public abstract class StatusViewData {
|
|||
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
|
||||
if (this.createdAt == null) createdAt = new Date();
|
||||
|
||||
return new StatusViewData.Concrete(id, content, reblogged, favourited, spoilerText, visibility,
|
||||
attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||
return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, spoilerText,
|
||||
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
||||
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
||||
statusEmojis, accountEmojis, card, isCollapsible, isCollapsed, poll, isBot);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue