Compare commits
2 Commits
d163e97c4f
...
a9f2c0202f
Author | SHA1 | Date |
---|---|---|
UlrichKu | a9f2c0202f | |
Lakoja | e6bbf9043b |
|
@ -194,7 +194,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter<RecyclerView.View
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
holder.showStatusContent(true);
|
holder.showStatusContent(true);
|
||||||
}
|
}
|
||||||
holder.setupWithStatus(status, statusListener, statusDisplayOptions, payloadForHolder);
|
holder.setupWithStatus(status, statusListener, statusDisplayOptions, payloadForHolder, true);
|
||||||
}
|
}
|
||||||
if (concreteNotification.getType() == Notification.Type.POLL) {
|
if (concreteNotification.getType() == Notification.Type.POLL) {
|
||||||
holder.setPollInfo(accountId.equals(concreteNotification.getAccount().getId()));
|
holder.setPollInfo(accountId.equals(concreteNotification.getAccount().getId()));
|
||||||
|
|
|
@ -769,14 +769,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupWithStatus(@NonNull StatusViewData.Concrete status, final @NonNull StatusActionListener listener,
|
public void setupWithStatus(@NonNull StatusViewData.Concrete status, final @NonNull StatusActionListener listener,
|
||||||
@NonNull StatusDisplayOptions statusDisplayOptions) {
|
@NonNull StatusDisplayOptions statusDisplayOptions, boolean showStatusInfo) {
|
||||||
this.setupWithStatus(status, listener, statusDisplayOptions, null);
|
this.setupWithStatus(status, listener, statusDisplayOptions, null, showStatusInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupWithStatus(@NonNull StatusViewData.Concrete status,
|
public void setupWithStatus(@NonNull StatusViewData.Concrete status,
|
||||||
@NonNull final StatusActionListener listener,
|
@NonNull final StatusActionListener listener,
|
||||||
@NonNull StatusDisplayOptions statusDisplayOptions,
|
@NonNull StatusDisplayOptions statusDisplayOptions,
|
||||||
@Nullable Object payloads) {
|
@Nullable Object payloads,
|
||||||
|
boolean showStatusInfo) {
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
Status actionable = status.getActionable();
|
Status actionable = status.getActionable();
|
||||||
setDisplayName(actionable.getAccount().getName(), actionable.getAccount().getEmojis(), statusDisplayOptions);
|
setDisplayName(actionable.getAccount().getName(), actionable.getAccount().getEmojis(), statusDisplayOptions);
|
||||||
|
|
|
@ -143,13 +143,14 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||||
public void setupWithStatus(@NonNull final StatusViewData.Concrete status,
|
public void setupWithStatus(@NonNull final StatusViewData.Concrete status,
|
||||||
@NonNull final StatusActionListener listener,
|
@NonNull final StatusActionListener listener,
|
||||||
@NonNull StatusDisplayOptions statusDisplayOptions,
|
@NonNull StatusDisplayOptions statusDisplayOptions,
|
||||||
@Nullable Object payloads) {
|
@Nullable Object payloads,
|
||||||
|
boolean showStatusInfo) {
|
||||||
// We never collapse statuses in the detail view
|
// We never collapse statuses in the detail view
|
||||||
StatusViewData.Concrete uncollapsedStatus = (status.isCollapsible() && status.isCollapsed()) ?
|
StatusViewData.Concrete uncollapsedStatus = (status.isCollapsible() && status.isCollapsed()) ?
|
||||||
status.copyWithCollapsed(false) :
|
status.copyWithCollapsed(false) :
|
||||||
status;
|
status;
|
||||||
|
|
||||||
super.setupWithStatus(uncollapsedStatus, listener, statusDisplayOptions, payloads);
|
super.setupWithStatus(uncollapsedStatus, listener, statusDisplayOptions, payloads, showStatusInfo);
|
||||||
setupCard(uncollapsedStatus, status.isExpanded(), CardViewMode.FULL_WIDTH, statusDisplayOptions, listener); // Always show card for detailed status
|
setupCard(uncollapsedStatus, status.isExpanded(), CardViewMode.FULL_WIDTH, statusDisplayOptions, listener); // Always show card for detailed status
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
Status actionable = uncollapsedStatus.getActionable();
|
Status actionable = uncollapsedStatus.getActionable();
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.entity.Emoji;
|
import com.keylesspalace.tusky.entity.Emoji;
|
||||||
import com.keylesspalace.tusky.entity.Filter;
|
import com.keylesspalace.tusky.entity.Filter;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
|
import com.keylesspalace.tusky.entity.TimelineAccount;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||||
import com.keylesspalace.tusky.util.NumberUtils;
|
import com.keylesspalace.tusky.util.NumberUtils;
|
||||||
|
@ -38,6 +39,7 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||||
import com.keylesspalace.tusky.util.StringUtils;
|
import com.keylesspalace.tusky.util.StringUtils;
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import at.connyduck.sparkbutton.helpers.Utils;
|
import at.connyduck.sparkbutton.helpers.Utils;
|
||||||
|
@ -63,21 +65,37 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||||
public void setupWithStatus(@NonNull StatusViewData.Concrete status,
|
public void setupWithStatus(@NonNull StatusViewData.Concrete status,
|
||||||
@NonNull final StatusActionListener listener,
|
@NonNull final StatusActionListener listener,
|
||||||
@NonNull StatusDisplayOptions statusDisplayOptions,
|
@NonNull StatusDisplayOptions statusDisplayOptions,
|
||||||
@Nullable Object payloads) {
|
@Nullable Object payloads,
|
||||||
|
boolean showStatusInfo) {
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
|
|
||||||
boolean sensitive = !TextUtils.isEmpty(status.getActionable().getSpoilerText());
|
boolean sensitive = !TextUtils.isEmpty(status.getActionable().getSpoilerText());
|
||||||
boolean expanded = status.isExpanded();
|
boolean expanded = status.isExpanded();
|
||||||
|
|
||||||
setupCollapsedState(sensitive, expanded, status, listener);
|
setupCollapsedState(sensitive, expanded, status, listener);
|
||||||
|
|
||||||
Status reblogging = status.getRebloggingStatus();
|
Status reblogging = status.getRebloggingStatus();
|
||||||
if (reblogging == null || status.getFilterAction() == Filter.Action.WARN) {
|
boolean isReply = status.getStatus().getInReplyToId() != null;
|
||||||
|
boolean isReplyOnly = isReply && reblogging == null;
|
||||||
|
|
||||||
|
boolean hasStatusContext = reblogging != null || isReply;
|
||||||
|
|
||||||
|
if (!hasStatusContext || !showStatusInfo || status.getFilterAction() == Filter.Action.WARN) {
|
||||||
hideStatusInfo();
|
hideStatusInfo();
|
||||||
} else {
|
} else {
|
||||||
String rebloggedByDisplayName = reblogging.getAccount().getName();
|
String accountName = "";
|
||||||
setRebloggedByDisplayName(rebloggedByDisplayName,
|
List<Emoji> emojis = Collections.emptyList();
|
||||||
reblogging.getAccount().getEmojis(), statusDisplayOptions);
|
if (reblogging != null) {
|
||||||
|
accountName = reblogging.getAccount().getName();
|
||||||
|
emojis = reblogging.getAccount().getEmojis();
|
||||||
|
} else if (isReply) {
|
||||||
|
TimelineAccount repliedTo = status.getInReplyToAccount();
|
||||||
|
if (repliedTo != null) {
|
||||||
|
accountName = repliedTo.getName();
|
||||||
|
emojis = repliedTo.getEmojis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatusInfoText(isReplyOnly, accountName, emojis, statusDisplayOptions);
|
||||||
statusInfo.setOnClickListener(v -> listener.onOpenReblog(getBindingAdapterPosition()));
|
statusInfo.setOnClickListener(v -> listener.onOpenReblog(getBindingAdapterPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,19 +106,27 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||||
setFavouritedCount(status.getActionable().getFavouritesCount());
|
setFavouritedCount(status.getActionable().getFavouritesCount());
|
||||||
setReblogsCount(status.getActionable().getReblogsCount());
|
setReblogsCount(status.getActionable().getReblogsCount());
|
||||||
|
|
||||||
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
|
super.setupWithStatus(status, listener, statusDisplayOptions, payloads, showStatusInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRebloggedByDisplayName(final CharSequence name,
|
private void setStatusInfoText(final boolean isReply,
|
||||||
|
final CharSequence name,
|
||||||
final List<Emoji> accountEmoji,
|
final List<Emoji> accountEmoji,
|
||||||
final StatusDisplayOptions statusDisplayOptions) {
|
final StatusDisplayOptions statusDisplayOptions) {
|
||||||
|
|
||||||
Context context = statusInfo.getContext();
|
Context context = statusInfo.getContext();
|
||||||
|
if (name.length() > 0) {
|
||||||
CharSequence wrappedName = StringUtils.unicodeWrap(name);
|
CharSequence wrappedName = StringUtils.unicodeWrap(name);
|
||||||
CharSequence boostedText = context.getString(R.string.post_boosted_format, wrappedName);
|
CharSequence statusContextText = context.getString(isReply ? R.string.post_replied_format : R.string.post_boosted_format, wrappedName);
|
||||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(
|
||||||
boostedText, accountEmoji, statusInfo, statusDisplayOptions.animateEmojis()
|
statusContextText, accountEmoji, statusInfo, statusDisplayOptions.animateEmojis()
|
||||||
);
|
);
|
||||||
statusInfo.setText(emojifiedText);
|
statusInfo.setText(emojifiedText);
|
||||||
|
} else {
|
||||||
|
statusInfo.setText(context.getString(R.string.post_replied));
|
||||||
|
}
|
||||||
|
statusInfo.setCompoundDrawablesWithIntrinsicBounds(isReply ? R.drawable.ic_reply_all_18dp : R.drawable.ic_reblog_18dp, 0, 0, 0);
|
||||||
|
|
||||||
statusInfo.setVisibility(View.VISIBLE);
|
statusInfo.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,8 @@ data class ConversationStatusEntity(
|
||||||
language = language,
|
language = language,
|
||||||
filtered = emptyList()
|
filtered = emptyList()
|
||||||
),
|
),
|
||||||
|
// TODO? implementation gap: not needed here atm, but inconsistent
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = expanded,
|
isExpanded = expanded,
|
||||||
isShowingContent = showingHiddenContent,
|
isShowingContent = showingHiddenContent,
|
||||||
isCollapsed = collapsed
|
isCollapsed = collapsed
|
||||||
|
|
|
@ -38,7 +38,7 @@ class SearchStatusesAdapter(
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: StatusViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: StatusViewHolder, position: Int) {
|
||||||
getItem(position)?.let { item ->
|
getItem(position)?.let { item ->
|
||||||
holder.setupWithStatus(item, statusListener, statusDisplayOptions)
|
holder.setupWithStatus(item, statusListener, statusDisplayOptions, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,8 @@ class TimelinePagingAdapter(
|
||||||
status,
|
status,
|
||||||
statusListener,
|
statusListener,
|
||||||
statusDisplayOptions,
|
statusDisplayOptions,
|
||||||
if (payloads != null && payloads.isNotEmpty()) payloads[0] else null
|
if (payloads != null && payloads.isNotEmpty()) payloads[0] else null,
|
||||||
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,8 +206,8 @@ fun TimelineStatusWithAccount.toViewData(moshi: Moshi, isDetailed: Boolean = fal
|
||||||
// no url for reblogs
|
// no url for reblogs
|
||||||
url = null,
|
url = null,
|
||||||
account = this.reblogAccount!!.toAccount(moshi),
|
account = this.reblogAccount!!.toAccount(moshi),
|
||||||
inReplyToId = null,
|
inReplyToId = status.inReplyToId,
|
||||||
inReplyToAccountId = null,
|
inReplyToAccountId = status.inReplyToAccountId,
|
||||||
reblog = reblog,
|
reblog = reblog,
|
||||||
content = "",
|
content = "",
|
||||||
// lie but whatever?
|
// lie but whatever?
|
||||||
|
@ -269,6 +269,7 @@ fun TimelineStatusWithAccount.toViewData(moshi: Moshi, isDetailed: Boolean = fal
|
||||||
}
|
}
|
||||||
return StatusViewData.Concrete(
|
return StatusViewData.Concrete(
|
||||||
status = status,
|
status = status,
|
||||||
|
inReplyToAccount = this.inReplyToAccount?.toAccount(moshi),
|
||||||
isExpanded = this.status.expanded,
|
isExpanded = this.status.expanded,
|
||||||
isShowingContent = this.status.contentShowing,
|
isShowingContent = this.status.contentShowing,
|
||||||
isCollapsed = this.status.contentCollapsed,
|
isCollapsed = this.status.contentCollapsed,
|
||||||
|
|
|
@ -20,19 +20,27 @@ import androidx.paging.ExperimentalPagingApi
|
||||||
import androidx.paging.LoadType
|
import androidx.paging.LoadType
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import androidx.paging.RemoteMediator
|
import androidx.paging.RemoteMediator
|
||||||
|
import com.keylesspalace.tusky.components.timeline.toAccount
|
||||||
import com.keylesspalace.tusky.components.timeline.util.ifExpected
|
import com.keylesspalace.tusky.components.timeline.util.ifExpected
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.db.AppDatabase
|
||||||
|
import com.keylesspalace.tusky.entity.TimelineAccount
|
||||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||||
import com.keylesspalace.tusky.util.toViewData
|
import com.keylesspalace.tusky.util.toViewData
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
|
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
class NetworkTimelineRemoteMediator(
|
class NetworkTimelineRemoteMediator(
|
||||||
private val accountManager: AccountManager,
|
private val accountManager: AccountManager,
|
||||||
private val viewModel: NetworkTimelineViewModel
|
private val viewModel: NetworkTimelineViewModel,
|
||||||
|
db: AppDatabase,
|
||||||
|
private val moshi: Moshi,
|
||||||
) : RemoteMediator<String, StatusViewData>() {
|
) : RemoteMediator<String, StatusViewData>() {
|
||||||
|
|
||||||
|
private val accountDao = db.timelineAccountDao()
|
||||||
|
|
||||||
private val statusIds = mutableSetOf<String>()
|
private val statusIds = mutableSetOf<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -80,7 +88,13 @@ class NetworkTimelineRemoteMediator(
|
||||||
val expanded = oldStatus?.isExpanded ?: activeAccount.alwaysOpenSpoiler
|
val expanded = oldStatus?.isExpanded ?: activeAccount.alwaysOpenSpoiler
|
||||||
val contentCollapsed = oldStatus?.isCollapsed ?: true
|
val contentCollapsed = oldStatus?.isCollapsed ?: true
|
||||||
|
|
||||||
|
var inReplyToAccount: TimelineAccount? = null
|
||||||
|
if (status.inReplyToAccountId != null) {
|
||||||
|
inReplyToAccount = accountDao.get(status.inReplyToAccountId)?.toAccount(moshi)
|
||||||
|
}
|
||||||
|
|
||||||
status.toViewData(
|
status.toViewData(
|
||||||
|
inReplyToAccount = inReplyToAccount,
|
||||||
isShowingContent = contentShowing,
|
isShowingContent = contentShowing,
|
||||||
isExpanded = expanded,
|
isExpanded = expanded,
|
||||||
isCollapsed = contentCollapsed
|
isCollapsed = contentCollapsed
|
||||||
|
|
|
@ -29,6 +29,7 @@ import at.connyduck.calladapter.networkresult.onFailure
|
||||||
import com.keylesspalace.tusky.appstore.EventHub
|
import com.keylesspalace.tusky.appstore.EventHub
|
||||||
import com.keylesspalace.tusky.components.timeline.util.ifExpected
|
import com.keylesspalace.tusky.components.timeline.util.ifExpected
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.db.AppDatabase
|
||||||
import com.keylesspalace.tusky.entity.Filter
|
import com.keylesspalace.tusky.entity.Filter
|
||||||
import com.keylesspalace.tusky.entity.Poll
|
import com.keylesspalace.tusky.entity.Poll
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
@ -41,6 +42,7 @@ import com.keylesspalace.tusky.util.isLessThanOrEqual
|
||||||
import com.keylesspalace.tusky.util.toViewData
|
import com.keylesspalace.tusky.util.toViewData
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
import com.keylesspalace.tusky.viewdata.TranslationViewData
|
import com.keylesspalace.tusky.viewdata.TranslationViewData
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -60,7 +62,9 @@ class NetworkTimelineViewModel @Inject constructor(
|
||||||
eventHub: EventHub,
|
eventHub: EventHub,
|
||||||
accountManager: AccountManager,
|
accountManager: AccountManager,
|
||||||
sharedPreferences: SharedPreferences,
|
sharedPreferences: SharedPreferences,
|
||||||
filterModel: FilterModel
|
filterModel: FilterModel,
|
||||||
|
private val db: AppDatabase,
|
||||||
|
private val moshi: Moshi,
|
||||||
) : TimelineViewModel(
|
) : TimelineViewModel(
|
||||||
timelineCases,
|
timelineCases,
|
||||||
api,
|
api,
|
||||||
|
@ -86,7 +90,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
||||||
currentSource = source
|
currentSource = source
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remoteMediator = NetworkTimelineRemoteMediator(accountManager, this)
|
remoteMediator = NetworkTimelineRemoteMediator(accountManager, this, db, moshi)
|
||||||
).flow
|
).flow
|
||||||
.map { pagingData ->
|
.map { pagingData ->
|
||||||
pagingData.filter(Dispatchers.Default.asExecutor()) { statusViewData ->
|
pagingData.filter(Dispatchers.Default.asExecutor()) { statusViewData ->
|
||||||
|
|
|
@ -53,7 +53,7 @@ class ThreadAdapter(
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: StatusBaseViewHolder, position: Int) {
|
override fun onBindViewHolder(viewHolder: StatusBaseViewHolder, position: Int) {
|
||||||
val status = getItem(position)
|
val status = getItem(position)
|
||||||
viewHolder.setupWithStatus(status, statusActionListener, statusDisplayOptions)
|
viewHolder.setupWithStatus(status, statusActionListener, statusDisplayOptions, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
|
|
@ -61,6 +61,7 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
@NonNull public abstract ConversationsDao conversationDao();
|
@NonNull public abstract ConversationsDao conversationDao();
|
||||||
@NonNull public abstract TimelineDao timelineDao();
|
@NonNull public abstract TimelineDao timelineDao();
|
||||||
@NonNull public abstract DraftDao draftDao();
|
@NonNull public abstract DraftDao draftDao();
|
||||||
|
@NonNull public abstract TimelineAccountDao timelineAccountDao();
|
||||||
|
|
||||||
public static final Migration MIGRATION_2_3 = new Migration(2, 3) {
|
public static final Migration MIGRATION_2_3 = new Migration(2, 3) {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* Copyright 2023 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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TimelineAccountDao {
|
||||||
|
@Query("SELECT * FROM TimelineAccountEntity WHERE serverId = :id")
|
||||||
|
suspend fun get(id: String): TimelineAccountEntity?
|
||||||
|
}
|
|
@ -44,17 +44,22 @@ s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.editedAt,
|
||||||
s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
||||||
s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId,
|
s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId,
|
||||||
s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, s.filtered,
|
s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, s.filtered,
|
||||||
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
|
author.serverId as 'author_serverId', author.timelineUserId as 'author_timelineUserId',
|
||||||
a.localUsername as 'a_localUsername', a.username as 'a_username',
|
author.localUsername as 'author_localUsername', author.username as 'author_username',
|
||||||
a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar',
|
author.displayName as 'author_displayName', author.url as 'author_url', author.avatar as 'author_avatar',
|
||||||
a.emojis as 'a_emojis', a.bot as 'a_bot',
|
author.emojis as 'author_emojis', author.bot as 'author_bot',
|
||||||
rb.serverId as 'rb_serverId', rb.timelineUserId 'rb_timelineUserId',
|
replied.serverId as 'replied_serverId', replied.timelineUserId 'replied_timelineUserId',
|
||||||
rb.localUsername as 'rb_localUsername', rb.username as 'rb_username',
|
replied.localUsername as 'replied_localUsername', replied.username as 'replied_username',
|
||||||
rb.displayName as 'rb_displayName', rb.url as 'rb_url', rb.avatar as 'rb_avatar',
|
replied.displayName as 'replied_displayName', replied.url as 'replied_url', replied.avatar as 'replied_avatar',
|
||||||
rb.emojis as 'rb_emojis', rb.bot as 'rb_bot'
|
replied.emojis as 'replied_emojis', replied.bot as 'replied_bot',
|
||||||
|
reblogger.serverId as 'reblogger_serverId', reblogger.timelineUserId 'reblogger_timelineUserId',
|
||||||
|
reblogger.localUsername as 'reblogger_localUsername', reblogger.username as 'reblogger_username',
|
||||||
|
reblogger.displayName as 'reblogger_displayName', reblogger.url as 'reblogger_url', reblogger.avatar as 'reblogger_avatar',
|
||||||
|
reblogger.emojis as 'reblogger_emojis', reblogger.bot as 'reblogger_bot'
|
||||||
FROM TimelineStatusEntity s
|
FROM TimelineStatusEntity s
|
||||||
LEFT JOIN TimelineAccountEntity a ON (s.timelineUserId = a.timelineUserId AND s.authorServerId = a.serverId)
|
LEFT JOIN TimelineAccountEntity author ON (s.timelineUserId = author.timelineUserId AND s.authorServerId = author.serverId)
|
||||||
LEFT JOIN TimelineAccountEntity rb ON (s.timelineUserId = rb.timelineUserId AND s.reblogAccountId = rb.serverId)
|
LEFT JOIN TimelineAccountEntity replied ON (s.inReplyToAccountId = replied.serverId)
|
||||||
|
LEFT JOIN TimelineAccountEntity reblogger ON (s.timelineUserId = reblogger.timelineUserId AND s.reblogAccountId = reblogger.serverId)
|
||||||
WHERE s.timelineUserId = :account
|
WHERE s.timelineUserId = :account
|
||||||
ORDER BY LENGTH(s.serverId) DESC, s.serverId DESC"""
|
ORDER BY LENGTH(s.serverId) DESC, s.serverId DESC"""
|
||||||
)
|
)
|
||||||
|
@ -67,17 +72,22 @@ s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.editedAt,
|
||||||
s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
s.emojis, s.reblogsCount, s.favouritesCount, s.repliesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive,
|
||||||
s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId,
|
s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId,
|
||||||
s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, s.filtered,
|
s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.language, s.filtered,
|
||||||
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
|
author.serverId as 'author_serverId', author.timelineUserId as 'author_timelineUserId',
|
||||||
a.localUsername as 'a_localUsername', a.username as 'a_username',
|
author.localUsername as 'author_localUsername', author.username as 'author_username',
|
||||||
a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar',
|
author.displayName as 'author_displayName', author.url as 'author_url', author.avatar as 'author_avatar',
|
||||||
a.emojis as 'a_emojis', a.bot as 'a_bot',
|
author.emojis as 'author_emojis', author.bot as 'author_bot',
|
||||||
rb.serverId as 'rb_serverId', rb.timelineUserId 'rb_timelineUserId',
|
replied.serverId as 'replied_serverId', replied.timelineUserId 'replied_timelineUserId',
|
||||||
rb.localUsername as 'rb_localUsername', rb.username as 'rb_username',
|
replied.localUsername as 'replied_localUsername', replied.username as 'replied_username',
|
||||||
rb.displayName as 'rb_displayName', rb.url as 'rb_url', rb.avatar as 'rb_avatar',
|
replied.displayName as 'replied_displayName', replied.url as 'replied_url', replied.avatar as 'replied_avatar',
|
||||||
rb.emojis as 'rb_emojis', rb.bot as 'rb_bot'
|
replied.emojis as 'replied_emojis', replied.bot as 'replied_bot',
|
||||||
|
reblogger.serverId as 'reblogger_serverId', reblogger.timelineUserId 'reblogger_timelineUserId',
|
||||||
|
reblogger.localUsername as 'reblogger_localUsername', reblogger.username as 'reblogger_username',
|
||||||
|
reblogger.displayName as 'reblogger_displayName', reblogger.url as 'reblogger_url', reblogger.avatar as 'reblogger_avatar',
|
||||||
|
reblogger.emojis as 'reblogger_emojis', reblogger.bot as 'reblogger_bot'
|
||||||
FROM TimelineStatusEntity s
|
FROM TimelineStatusEntity s
|
||||||
LEFT JOIN TimelineAccountEntity a ON (s.timelineUserId = a.timelineUserId AND s.authorServerId = a.serverId)
|
LEFT JOIN TimelineAccountEntity author ON (s.timelineUserId = author.timelineUserId AND s.authorServerId = author.serverId)
|
||||||
LEFT JOIN TimelineAccountEntity rb ON (s.timelineUserId = rb.timelineUserId AND s.reblogAccountId = rb.serverId)
|
LEFT JOIN TimelineAccountEntity replied ON (s.inReplyToAccountId = replied.serverId)
|
||||||
|
LEFT JOIN TimelineAccountEntity reblogger ON (s.timelineUserId = reblogger.timelineUserId AND s.reblogAccountId = reblogger.serverId)
|
||||||
WHERE (s.serverId = :statusId OR s.reblogServerId = :statusId)
|
WHERE (s.serverId = :statusId OR s.reblogServerId = :statusId)
|
||||||
AND s.authorServerId IS NOT NULL
|
AND s.authorServerId IS NOT NULL
|
||||||
AND s.timelineUserId = :accountId"""
|
AND s.timelineUserId = :accountId"""
|
||||||
|
|
|
@ -111,10 +111,16 @@ data class TimelineAccountEntity(
|
||||||
data class TimelineStatusWithAccount(
|
data class TimelineStatusWithAccount(
|
||||||
@Embedded
|
@Embedded
|
||||||
val status: TimelineStatusEntity,
|
val status: TimelineStatusEntity,
|
||||||
|
|
||||||
// null when placeholder
|
// null when placeholder
|
||||||
@Embedded(prefix = "a_")
|
@Embedded(prefix = "author_")
|
||||||
val account: TimelineAccountEntity? = null,
|
val account: TimelineAccountEntity? = null,
|
||||||
|
|
||||||
|
// null when no reply
|
||||||
|
@Embedded(prefix = "replied_")
|
||||||
|
val inReplyToAccount: TimelineAccountEntity? = null,
|
||||||
|
|
||||||
// null when no reblog
|
// null when no reblog
|
||||||
@Embedded(prefix = "rb_")
|
@Embedded(prefix = "reblogger_")
|
||||||
val reblogAccount: TimelineAccountEntity? = null
|
val reblogAccount: TimelineAccountEntity? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -38,6 +38,7 @@ import androidx.paging.CombinedLoadStates
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import com.keylesspalace.tusky.entity.Notification
|
import com.keylesspalace.tusky.entity.Notification
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.entity.TimelineAccount
|
||||||
import com.keylesspalace.tusky.entity.TrendingTag
|
import com.keylesspalace.tusky.entity.TrendingTag
|
||||||
import com.keylesspalace.tusky.viewdata.NotificationViewData
|
import com.keylesspalace.tusky.viewdata.NotificationViewData
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
|
@ -49,10 +50,12 @@ fun Status.toViewData(
|
||||||
isExpanded: Boolean,
|
isExpanded: Boolean,
|
||||||
isCollapsed: Boolean,
|
isCollapsed: Boolean,
|
||||||
isDetailed: Boolean = false,
|
isDetailed: Boolean = false,
|
||||||
|
inReplyToAccount: TimelineAccount? = null,
|
||||||
translation: TranslationViewData? = null,
|
translation: TranslationViewData? = null,
|
||||||
): StatusViewData.Concrete {
|
): StatusViewData.Concrete {
|
||||||
return StatusViewData.Concrete(
|
return StatusViewData.Concrete(
|
||||||
status = this,
|
status = this,
|
||||||
|
inReplyToAccount = inReplyToAccount,
|
||||||
isShowingContent = isShowingContent,
|
isShowingContent = isShowingContent,
|
||||||
isCollapsed = isCollapsed,
|
isCollapsed = isCollapsed,
|
||||||
isExpanded = isExpanded,
|
isExpanded = isExpanded,
|
||||||
|
@ -71,6 +74,7 @@ fun Notification.toViewData(
|
||||||
this.type,
|
this.type,
|
||||||
this.id,
|
this.id,
|
||||||
this.account,
|
this.account,
|
||||||
|
// TODO? account null implementation gap; and other locations:
|
||||||
this.status?.toViewData(isShowingContent, isExpanded, isCollapsed),
|
this.status?.toViewData(isShowingContent, isExpanded, isCollapsed),
|
||||||
this.report
|
this.report
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import android.text.Spanned
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Filter
|
import com.keylesspalace.tusky.entity.Filter
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.entity.TimelineAccount
|
||||||
import com.keylesspalace.tusky.entity.Translation
|
import com.keylesspalace.tusky.entity.Translation
|
||||||
import com.keylesspalace.tusky.util.parseAsMastodonHtml
|
import com.keylesspalace.tusky.util.parseAsMastodonHtml
|
||||||
import com.keylesspalace.tusky.util.shouldTrimStatus
|
import com.keylesspalace.tusky.util.shouldTrimStatus
|
||||||
|
@ -45,6 +46,7 @@ sealed class StatusViewData {
|
||||||
|
|
||||||
data class Concrete(
|
data class Concrete(
|
||||||
val status: Status,
|
val status: Status,
|
||||||
|
val inReplyToAccount: TimelineAccount?,
|
||||||
val isExpanded: Boolean,
|
val isExpanded: Boolean,
|
||||||
val isShowingContent: Boolean,
|
val isShowingContent: Boolean,
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M7,8L7,5l-7,7 7,7v-3l-4,-4 4,-4zM13,9L13,5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
|
||||||
|
</vector>
|
|
@ -29,7 +29,8 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="RtlSymmetry"
|
tools:ignore="RtlSymmetry"
|
||||||
tools:text="ConnyDuck boosted"
|
tools:text="ConnyDuck boosted"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible"
|
||||||
|
app:drawableTint="?android:textColorTertiary" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/status_avatar"
|
android:id="@+id/status_avatar"
|
||||||
|
|
|
@ -84,6 +84,8 @@
|
||||||
|
|
||||||
<string name="post_username_format">\@%s</string>
|
<string name="post_username_format">\@%s</string>
|
||||||
<string name="post_boosted_format">%s boosted</string>
|
<string name="post_boosted_format">%s boosted</string>
|
||||||
|
<string name="post_replied">Replied</string>
|
||||||
|
<string name="post_replied_format">In reply to %s</string>
|
||||||
<string name="post_sensitive_media_title">Sensitive content</string>
|
<string name="post_sensitive_media_title">Sensitive content</string>
|
||||||
<string name="post_media_hidden_title">Media hidden</string>
|
<string name="post_media_hidden_title">Media hidden</string>
|
||||||
<string name="post_media_alt">ALT</string>
|
<string name="post_media_alt">ALT</string>
|
||||||
|
|
|
@ -44,12 +44,14 @@ class StatusComparisonTest {
|
||||||
fun `two equal status view data - should be equal`() {
|
fun `two equal status view data - should be equal`() {
|
||||||
val viewdata1 = StatusViewData.Concrete(
|
val viewdata1 = StatusViewData.Concrete(
|
||||||
status = createStatus(),
|
status = createStatus(),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = false,
|
isExpanded = false,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
)
|
)
|
||||||
val viewdata2 = StatusViewData.Concrete(
|
val viewdata2 = StatusViewData.Concrete(
|
||||||
status = createStatus(),
|
status = createStatus(),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = false,
|
isExpanded = false,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
|
@ -61,12 +63,14 @@ class StatusComparisonTest {
|
||||||
fun `status view data with different isExpanded - should not be equal`() {
|
fun `status view data with different isExpanded - should not be equal`() {
|
||||||
val viewdata1 = StatusViewData.Concrete(
|
val viewdata1 = StatusViewData.Concrete(
|
||||||
status = createStatus(),
|
status = createStatus(),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = true,
|
isExpanded = true,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
)
|
)
|
||||||
val viewdata2 = StatusViewData.Concrete(
|
val viewdata2 = StatusViewData.Concrete(
|
||||||
status = createStatus(),
|
status = createStatus(),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = false,
|
isExpanded = false,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
|
@ -78,12 +82,14 @@ class StatusComparisonTest {
|
||||||
fun `status view data with different statuses- should not be equal`() {
|
fun `status view data with different statuses- should not be equal`() {
|
||||||
val viewdata1 = StatusViewData.Concrete(
|
val viewdata1 = StatusViewData.Concrete(
|
||||||
status = createStatus(content = "whatever"),
|
status = createStatus(content = "whatever"),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = true,
|
isExpanded = true,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
)
|
)
|
||||||
val viewdata2 = StatusViewData.Concrete(
|
val viewdata2 = StatusViewData.Concrete(
|
||||||
status = createStatus(),
|
status = createStatus(),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = false,
|
isExpanded = false,
|
||||||
isShowingContent = false,
|
isShowingContent = false,
|
||||||
isCollapsed = false
|
isCollapsed = false
|
||||||
|
@ -104,7 +110,7 @@ class StatusComparisonTest {
|
||||||
"id": "$id",
|
"id": "$id",
|
||||||
"created_at": "2022-02-26T09:54:45.000Z",
|
"created_at": "2022-02-26T09:54:45.000Z",
|
||||||
"in_reply_to_id": null,
|
"in_reply_to_id": null,
|
||||||
"in_reply_to_account_id": null,
|
"in_reply_to_account": null,
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
"spoiler_text": "",
|
"spoiler_text": "",
|
||||||
"visibility": "public",
|
"visibility": "public",
|
||||||
|
|
|
@ -12,7 +12,10 @@ import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineView
|
||||||
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel
|
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel
|
||||||
import com.keylesspalace.tusky.db.AccountEntity
|
import com.keylesspalace.tusky.db.AccountEntity
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
|
import com.keylesspalace.tusky.db.AppDatabase
|
||||||
|
import com.keylesspalace.tusky.db.TimelineAccountDao
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
|
@ -21,6 +24,7 @@ import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.any
|
||||||
import org.mockito.kotlin.anyOrNull
|
import org.mockito.kotlin.anyOrNull
|
||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.doThrow
|
import org.mockito.kotlin.doThrow
|
||||||
|
@ -45,6 +49,14 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val accountDao: TimelineAccountDao = mock {
|
||||||
|
onBlocking { get(any()) } doReturn null
|
||||||
|
}
|
||||||
|
private val db: AppDatabase = mock {
|
||||||
|
on { timelineAccountDao() } doReturn accountDao
|
||||||
|
}
|
||||||
|
private val moshi: Moshi = mock {}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ExperimentalPagingApi
|
@ExperimentalPagingApi
|
||||||
fun `should return error when network call returns error code`() {
|
fun `should return error when network call returns error code`() {
|
||||||
|
@ -53,7 +65,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
onBlocking { fetchStatusesForKind(anyOrNull(), anyOrNull(), anyOrNull()) } doReturn Response.error(500, "".toResponseBody())
|
onBlocking { fetchStatusesForKind(anyOrNull(), anyOrNull(), anyOrNull()) } doReturn Response.error(500, "".toResponseBody())
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val result = runBlocking { remoteMediator.load(LoadType.REFRESH, state()) }
|
val result = runBlocking { remoteMediator.load(LoadType.REFRESH, state()) }
|
||||||
|
|
||||||
|
@ -70,7 +82,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
onBlocking { fetchStatusesForKind(anyOrNull(), anyOrNull(), anyOrNull()) } doThrow IOException()
|
onBlocking { fetchStatusesForKind(anyOrNull(), anyOrNull(), anyOrNull()) } doThrow IOException()
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val result = runBlocking { remoteMediator.load(LoadType.REFRESH, state()) }
|
val result = runBlocking { remoteMediator.load(LoadType.REFRESH, state()) }
|
||||||
|
|
||||||
|
@ -99,7 +111,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -146,7 +158,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -198,7 +210,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -251,7 +263,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -308,7 +320,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -354,7 +366,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
on { nextKey } doReturn null
|
on { nextKey } doReturn null
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -409,7 +421,7 @@ class NetworkTimelineRemoteMediatorTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
|
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel, db, moshi)
|
||||||
|
|
||||||
val state = state(
|
val state = state(
|
||||||
listOf(
|
listOf(
|
||||||
|
|
|
@ -79,6 +79,7 @@ fun mockStatusViewData(
|
||||||
favourited = favourited,
|
favourited = favourited,
|
||||||
bookmarked = bookmarked
|
bookmarked = bookmarked
|
||||||
),
|
),
|
||||||
|
inReplyToAccount = null,
|
||||||
isExpanded = isExpanded,
|
isExpanded = isExpanded,
|
||||||
isShowingContent = isShowingContent,
|
isShowingContent = isShowingContent,
|
||||||
isCollapsed = isCollapsed,
|
isCollapsed = isCollapsed,
|
||||||
|
|
|
@ -108,6 +108,7 @@ glide-animation-plugin = { module = "com.github.penfeizhou.android.animation:gli
|
||||||
glide-compiler = { module = "com.github.bumptech.glide:ksp", version.ref = "glide" }
|
glide-compiler = { module = "com.github.bumptech.glide:ksp", version.ref = "glide" }
|
||||||
glide-core = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
glide-core = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||||
glide-okhttp3-integration = { module = "com.github.bumptech.glide:okhttp3-integration", version.ref = "glide" }
|
glide-okhttp3-integration = { module = "com.github.bumptech.glide:okhttp3-integration", version.ref = "glide" }
|
||||||
|
kapt = { module = "androidx.room:room-compiler", version.ref = "androidx-room" }
|
||||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||||
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||||
image-cropper = { module = "com.github.CanHub:Android-Image-Cropper", version.ref = "image-cropper" }
|
image-cropper = { module = "com.github.CanHub:Android-Image-Cropper", version.ref = "image-cropper" }
|
||||||
|
|
Loading…
Reference in New Issue