From b4eda5ea65e71419233b6a350ade04baf4c95e1f Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Thu, 5 May 2022 18:27:05 +0200 Subject: [PATCH] Unbreak link previews in timelines (#2506) --- .../timeline/TimelineTypeMappers.kt | 12 ++++--- .../keylesspalace/tusky/db/AppDatabase.java | 9 +++++- .../com/keylesspalace/tusky/db/TimelineDao.kt | 2 +- .../tusky/db/TimelineStatusEntity.kt | 3 +- .../com/keylesspalace/tusky/di/AppModule.kt | 2 +- .../keylesspalace/tusky/db/TimelineDaoTest.kt | 32 +++++++++++++++++-- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt index 6ec95423..12422a95 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt @@ -21,6 +21,7 @@ import com.keylesspalace.tusky.db.TimelineAccountEntity import com.keylesspalace.tusky.db.TimelineStatusEntity import com.keylesspalace.tusky.db.TimelineStatusWithAccount import com.keylesspalace.tusky.entity.Attachment +import com.keylesspalace.tusky.entity.Card import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.entity.Poll @@ -96,7 +97,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity { expanded = loading, contentCollapsed = false, contentShowing = false, - pinned = false + pinned = false, + card = null, ) } @@ -136,7 +138,8 @@ fun Status.toEntity( expanded = expanded, contentShowing = contentShowing, 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 emojis: List = gson.fromJson(status.emojis, emojisListType) ?: emptyList() 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 -> Status( @@ -178,7 +182,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { pinned = false, muted = status.muted, poll = poll, - card = null + card = card, ) } val status = if (reblog != null) { @@ -235,7 +239,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { pinned = status.pinned, muted = status.muted, poll = poll, - card = null + card = card, ) } return StatusViewData.Concrete( diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 293db65e..d5f023e5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -31,7 +31,7 @@ import java.io.File; */ @Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class, TimelineAccountEntity.class, ConversationEntity.class - }, version = 34) + }, version = 35) public abstract class AppDatabase extends RoomDatabase { 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"); } }; + + 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"); + } + }; } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index dd59f2a3..2c6ef188 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -36,7 +36,7 @@ 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.bookmarked, s.sensitive, 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.localUsername as 'a_localUsername', a.username as 'a_username', a.displayName as 'a_displayName', a.url as 'a_url', a.avatar as 'a_avatar', diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt index 41b122c3..2c4d45c3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt @@ -78,7 +78,8 @@ data class TimelineStatusEntity( val expanded: Boolean, // used as the "loading" attribute when this TimelineStatusEntity is a placeholder val contentCollapsed: Boolean, val contentShowing: Boolean, - val pinned: Boolean + val pinned: Boolean, + val card: String?, ) @Entity( diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt index c92d52ef..0861e9cf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt @@ -63,7 +63,7 @@ class AppModule { AppDatabase.Migration25_26(appContext.getExternalFilesDir("Tusky")), 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_32_33, AppDatabase.MIGRATION_33_34 + AppDatabase.MIGRATION_32_33, AppDatabase.MIGRATION_33_34, AppDatabase.MIGRATION_34_35, ) .build() } diff --git a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt index 889e5f98..ed652418 100644 --- a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt @@ -369,13 +369,36 @@ class TimelineDaoTest { 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( accountId: Long = 1, statusId: Long = 10, reblog: Boolean = false, createdAt: Long = statusId, authorServerId: String = "20", - domain: String = "mastodon.example" + domain: String = "mastodon.example", + cardUrl: String? = null, ): Triple { val author = TimelineAccountEntity( serverId = authorServerId, @@ -403,6 +426,10 @@ class TimelineDaoTest { ) } else null + val card = when (cardUrl) { + null -> null + else -> "{ url: \"$cardUrl\" }" + } val even = accountId % 2 == 0L val status = TimelineStatusEntity( serverId = statusId.toString(), @@ -433,7 +460,8 @@ class TimelineDaoTest { expanded = false, contentCollapsed = false, contentShowing = true, - pinned = false + pinned = false, + card = card, ) return Triple(status, author, reblogAuthor) }