Unbreak link previews in timelines (#2506)

This commit is contained in:
Levi Bard 2022-05-05 18:27:05 +02:00 committed by GitHub
parent db81ede04a
commit b4eda5ea65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 50 additions and 10 deletions

View file

@ -21,6 +21,7 @@ import com.keylesspalace.tusky.db.TimelineAccountEntity
import com.keylesspalace.tusky.db.TimelineStatusEntity import com.keylesspalace.tusky.db.TimelineStatusEntity
import com.keylesspalace.tusky.db.TimelineStatusWithAccount import com.keylesspalace.tusky.db.TimelineStatusWithAccount
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Card
import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.entity.Poll import com.keylesspalace.tusky.entity.Poll
@ -96,7 +97,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
expanded = loading, expanded = loading,
contentCollapsed = false, contentCollapsed = false,
contentShowing = false, contentShowing = false,
pinned = false pinned = false,
card = null,
) )
} }
@ -136,7 +138,8 @@ fun Status.toEntity(
expanded = expanded, expanded = expanded,
contentShowing = contentShowing, contentShowing = contentShowing,
contentCollapsed = contentCollapsed, contentCollapsed = contentCollapsed,
pinned = actionableStatus.pinned == true pinned = actionableStatus.pinned == true,
card = actionableStatus.card?.let(gson::toJson),
) )
} }
@ -151,6 +154,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
val application = gson.fromJson(status.application, Status.Application::class.java) val application = gson.fromJson(status.application, Status.Application::class.java)
val emojis: List<Emoji> = gson.fromJson(status.emojis, emojisListType) ?: emptyList() val emojis: List<Emoji> = gson.fromJson(status.emojis, emojisListType) ?: emptyList()
val poll: Poll? = gson.fromJson(status.poll, Poll::class.java) val poll: Poll? = gson.fromJson(status.poll, Poll::class.java)
val card: Card? = gson.fromJson(status.card, Card::class.java)
val reblog = status.reblogServerId?.let { id -> val reblog = status.reblogServerId?.let { id ->
Status( Status(
@ -178,7 +182,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
pinned = false, pinned = false,
muted = status.muted, muted = status.muted,
poll = poll, poll = poll,
card = null card = card,
) )
} }
val status = if (reblog != null) { val status = if (reblog != null) {
@ -235,7 +239,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData {
pinned = status.pinned, pinned = status.pinned,
muted = status.muted, muted = status.muted,
poll = poll, poll = poll,
card = null card = card,
) )
} }
return StatusViewData.Concrete( return StatusViewData.Concrete(

View file

@ -31,7 +31,7 @@ import java.io.File;
*/ */
@Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class, @Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
TimelineAccountEntity.class, ConversationEntity.class TimelineAccountEntity.class, ConversationEntity.class
}, version = 34) }, version = 35)
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
public abstract AccountDao accountDao(); public abstract AccountDao accountDao();
@ -534,4 +534,11 @@ public abstract class AppDatabase extends RoomDatabase {
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsUpdates` INTEGER NOT NULL DEFAULT 1"); database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsUpdates` INTEGER NOT NULL DEFAULT 1");
} }
}; };
public static final Migration MIGRATION_34_35 = new Migration(34, 35) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `card` TEXT");
}
};
} }

View file

@ -36,7 +36,7 @@ SELECT s.serverId, s.url, s.timelineUserId,
s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt,
s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive, s.emojis, s.reblogsCount, s.favouritesCount, 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.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned,
a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId', a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId',
a.localUsername as 'a_localUsername', a.username as 'a_username', a.localUsername as 'a_localUsername', a.username as 'a_username',
a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar', a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar',

View file

@ -78,7 +78,8 @@ data class TimelineStatusEntity(
val expanded: Boolean, // used as the "loading" attribute when this TimelineStatusEntity is a placeholder val expanded: Boolean, // used as the "loading" attribute when this TimelineStatusEntity is a placeholder
val contentCollapsed: Boolean, val contentCollapsed: Boolean,
val contentShowing: Boolean, val contentShowing: Boolean,
val pinned: Boolean val pinned: Boolean,
val card: String?,
) )
@Entity( @Entity(

View file

@ -63,7 +63,7 @@ class AppModule {
AppDatabase.Migration25_26(appContext.getExternalFilesDir("Tusky")), AppDatabase.Migration25_26(appContext.getExternalFilesDir("Tusky")),
AppDatabase.MIGRATION_26_27, AppDatabase.MIGRATION_27_28, AppDatabase.MIGRATION_28_29, AppDatabase.MIGRATION_26_27, AppDatabase.MIGRATION_27_28, AppDatabase.MIGRATION_28_29,
AppDatabase.MIGRATION_29_30, AppDatabase.MIGRATION_30_31, AppDatabase.MIGRATION_31_32, AppDatabase.MIGRATION_29_30, AppDatabase.MIGRATION_30_31, AppDatabase.MIGRATION_31_32,
AppDatabase.MIGRATION_32_33, AppDatabase.MIGRATION_33_34 AppDatabase.MIGRATION_32_33, AppDatabase.MIGRATION_33_34, AppDatabase.MIGRATION_34_35,
) )
.build() .build()
} }

View file

@ -369,13 +369,36 @@ class TimelineDaoTest {
assertEquals("99", timelineDao.getTopPlaceholderId(1)) assertEquals("99", timelineDao.getTopPlaceholderId(1))
} }
@Test
fun `preview card survives roundtrip`() = runBlocking {
val setOne = makeStatus(statusId = 3, cardUrl = "https://foo.bar")
for ((status, author, reblogger) in listOf(setOne)) {
timelineDao.insertAccount(author)
reblogger?.let {
timelineDao.insertAccount(it)
}
timelineDao.insertStatus(status)
}
val pagingSource = timelineDao.getStatuses(setOne.first.timelineUserId)
val loadResult = pagingSource.load(PagingSource.LoadParams.Refresh(null, 2, false))
val loadedStatuses = (loadResult as PagingSource.LoadResult.Page).data
assertEquals(1, loadedStatuses.size)
assertStatuses(listOf(setOne), loadedStatuses)
}
private fun makeStatus( private fun makeStatus(
accountId: Long = 1, accountId: Long = 1,
statusId: Long = 10, statusId: Long = 10,
reblog: Boolean = false, reblog: Boolean = false,
createdAt: Long = statusId, createdAt: Long = statusId,
authorServerId: String = "20", authorServerId: String = "20",
domain: String = "mastodon.example" domain: String = "mastodon.example",
cardUrl: String? = null,
): Triple<TimelineStatusEntity, TimelineAccountEntity, TimelineAccountEntity?> { ): Triple<TimelineStatusEntity, TimelineAccountEntity, TimelineAccountEntity?> {
val author = TimelineAccountEntity( val author = TimelineAccountEntity(
serverId = authorServerId, serverId = authorServerId,
@ -403,6 +426,10 @@ class TimelineDaoTest {
) )
} else null } else null
val card = when (cardUrl) {
null -> null
else -> "{ url: \"$cardUrl\" }"
}
val even = accountId % 2 == 0L val even = accountId % 2 == 0L
val status = TimelineStatusEntity( val status = TimelineStatusEntity(
serverId = statusId.toString(), serverId = statusId.toString(),
@ -433,7 +460,8 @@ class TimelineDaoTest {
expanded = false, expanded = false,
contentCollapsed = false, contentCollapsed = false,
contentShowing = true, contentShowing = true,
pinned = false pinned = false,
card = card,
) )
return Triple(status, author, reblogAuthor) return Triple(status, author, reblogAuthor)
} }