Integrating data layer DAOs

Change-Id: I6bebf14768841f0fc9a224d933c059cfc50f9943
pull/2/head
Adetunji Dahunsi 3 years ago
parent 1411b1576b
commit 9651c6a581

@ -23,15 +23,15 @@ import com.google.samples.apps.nowinandroid.core.database.dao.AuthorDao
import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao import com.google.samples.apps.nowinandroid.core.database.dao.EpisodeDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.util.InstantConverter import com.google.samples.apps.nowinandroid.core.database.util.InstantConverter
import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeConverter import com.google.samples.apps.nowinandroid.core.database.util.NewsResourceTypeConverter
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
@Database( @Database(
entities = [ entities = [

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**

@ -19,8 +19,9 @@ package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedEpisode
import com.google.samples.apps.nowinandroid.core.model.data.Episode import com.google.samples.apps.nowinandroid.core.model.data.Episode
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@ -29,7 +30,7 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface EpisodeDao { interface EpisodeDao {
@Query(value = "SELECT * FROM episodes") @Query(value = "SELECT * FROM episodes")
fun getEpisodesStream(): Flow<List<Episode>> fun getEpisodesStream(): Flow<List<PopulatedEpisode>>
@Insert @Insert
suspend fun saveEpisodeEntities(entities: List<EpisodeEntity>) suspend fun saveEpisodeEntities(entities: List<EpisodeEntity>)

@ -19,8 +19,9 @@ package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@ -29,7 +30,7 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface NewsResourceDao { interface NewsResourceDao {
@Query(value = "SELECT * FROM news_resources") @Query(value = "SELECT * FROM news_resources")
fun getNewsResourcesStream(): Flow<List<NewsResource>> fun getNewsResourcesStream(): Flow<List<PopulatedNewsResource>>
@Query( @Query(
value = """ value = """
@ -37,7 +38,7 @@ interface NewsResourceDao {
WHERE id IN (:filterTopicIds) WHERE id IN (:filterTopicIds)
""" """
) )
fun getNewsResourcesStream(filterTopicIds: Set<Int>): Flow<List<NewsResource>> fun getNewsResourcesStream(filterTopicIds: Set<Int>): Flow<List<PopulatedNewsResource>>
@Insert @Insert
suspend fun saveNewsResourceEntities(entities: List<NewsResourceEntity>) suspend fun saveNewsResourceEntities(entities: List<NewsResourceEntity>)

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.model.data.Author
/** /**
* Defines an author for either an [EpisodeEntity] or [NewsResourceEntity]. * Defines an author for either an [EpisodeEntity] or [NewsResourceEntity].
@ -38,3 +39,9 @@ data class AuthorEntity(
@ColumnInfo(name = "image_url") @ColumnInfo(name = "image_url")
val imageUrl: String, val imageUrl: String,
) )
fun AuthorEntity.asExternalModel() = Author(
id = id,
name = name,
imageUrl = imageUrl,
)

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity

@ -14,12 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
/** /**
@ -47,5 +49,17 @@ data class NewsResourceEntity(
val url: String, val url: String,
@ColumnInfo(name = "publish_date") @ColumnInfo(name = "publish_date")
val publishDate: Instant, val publishDate: Instant,
val type: String, val type: NewsResourceType,
)
fun NewsResourceEntity.asExternalModel() = NewsResource(
id = id,
episodeId = episodeId,
title = title,
content = content,
url = url,
publishDate = publishDate,
type = type,
authors = listOf(),
topics = listOf()
) )

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity

@ -0,0 +1,55 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.core.model.data.Episode
/**
* External data layer representation of an NiA episode
*/
data class PopulatedEpisode(
@Embedded
val entity: EpisodeEntity,
@Relation(
parentColumn = "id",
entityColumn = "episode_id"
)
val newsResources: List<NewsResourceEntity>,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = EpisodeAuthorCrossRef::class,
parentColumn = "episode_id",
entityColumn = "author_id",
)
)
val authors: List<AuthorEntity>
)
fun PopulatedEpisode.asExternalModel() = Episode(
id = entity.id,
name = entity.name,
publishDate = entity.publishDate,
alternateVideo = entity.alternateVideo,
alternateAudio = entity.alternateAudio,
newsResources = newsResources.map(NewsResourceEntity::asExternalModel),
authors = authors.map(AuthorEntity::asExternalModel)
)

@ -0,0 +1,67 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
/**
* External data layer representation of a fully populated NiA news resource
*/
data class PopulatedNewsResource(
@Embedded
val entity: NewsResourceEntity,
@Relation(
parentColumn = "episode_id",
entityColumn = "id"
)
val episode: EpisodeEntity,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = NewsResourceAuthorCrossRef::class,
parentColumn = "news_resource_id",
entityColumn = "author_id",
)
)
val authors: List<AuthorEntity>,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = NewsResourceTopicCrossRef::class,
parentColumn = "news_resource_id",
entityColumn = "topic_id",
)
)
val topics: List<TopicEntity>
)
fun PopulatedNewsResource.asExternalModel() = NewsResource(
id = entity.id,
episodeId = entity.episodeId,
title = entity.title,
content = entity.content,
url = entity.url,
publishDate = entity.publishDate,
type = entity.type,
authors = authors.map(AuthorEntity::asExternalModel),
topics = topics.map(TopicEntity::asExternalModel)
)

@ -14,11 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.entities package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.model.data.Topic
/** /**
* Defines a topic a user may follow. * Defines a topic a user may follow.
@ -37,3 +38,10 @@ data class TopicEntity(
val description: String, val description: String,
val followed: Boolean, val followed: Boolean,
) )
fun TopicEntity.asExternalModel() = Topic(
id = id,
name = name,
description = description,
followed = followed,
)

@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid.core.database.util
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.asNewsResourceType
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
class InstantConverter { class InstantConverter {
@ -36,10 +37,5 @@ class NewsResourceTypeConverter {
value?.let(NewsResourceType::name) value?.let(NewsResourceType::name)
@TypeConverter @TypeConverter
fun stringToNewsResourceType(name: String?): NewsResourceType = when (name) { fun stringToNewsResourceType(name: String?): NewsResourceType = name.asNewsResourceType()
null -> NewsResourceType.Unknown
else -> NewsResourceType.values()
.firstOrNull { type -> type.name == name }
?: NewsResourceType.Unknown
}
} }

@ -39,11 +39,13 @@ android {
dependencies { dependencies {
implementation project(':core-model') implementation project(':core-model')
implementation project(':core-database')
implementation project(':core-datastore') implementation project(':core-datastore')
implementation project(':core-network') implementation project(':core-network')
testImplementation project(':core-testing') testImplementation project(':core-testing')
implementation libs.kotlinx.datetime
implementation libs.kotlinx.coroutines.android implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.serialization.json implementation libs.kotlinx.serialization.json

@ -0,0 +1,26 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.database.model.AuthorEntity
import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
fun NetworkAuthor.asEntity() = AuthorEntity(
id = id,
name = name,
imageUrl = imageUrl
)

@ -0,0 +1,37 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.database.model.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisode
import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisodeExpanded
fun NetworkEpisode.asEntity() = EpisodeEntity(
id = id,
name = name,
publishDate = publishDate,
alternateVideo = alternateVideo,
alternateAudio = alternateAudio,
)
fun NetworkEpisodeExpanded.asEntity() = EpisodeEntity(
id = id,
name = name,
publishDate = publishDate,
alternateVideo = alternateVideo,
alternateAudio = alternateAudio,
)

@ -0,0 +1,41 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded
fun NetworkNewsResource.asEntity() = NewsResourceEntity(
id = id,
episodeId = episodeId,
title = title,
content = content,
url = url,
publishDate = publishDate,
type = type,
)
fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity(
id = id,
episodeId = episodeId,
title = title,
content = content,
url = url,
publishDate = publishDate,
type = type,
)

@ -0,0 +1,27 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
fun NetworkTopic.asEntity() = TopicEntity(
id = id,
name = name,
description = description,
followed = followed
)

@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferences
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource import com.google.samples.apps.nowinandroid.core.network.fake.FakeDataSource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow

@ -0,0 +1,90 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.model.data.Episode
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import kotlinx.datetime.Instant
import org.junit.Assert.assertEquals
import org.junit.Test
class PopulatedEpisodeKtTest {
@Test
fun populated_episode_can_be_mapped_to_episode() {
val populatedEpisode = PopulatedEpisode(
entity = EpisodeEntity(
id = 0,
name = "Test",
publishDate = Instant.fromEpochMilliseconds(1),
alternateAudio = "audio",
alternateVideo = "video"
),
newsResources = listOf(
NewsResourceEntity(
id = 1,
episodeId = 0,
title = "news",
content = "Hilt",
url = "url",
type = Video,
publishDate = Instant.fromEpochMilliseconds(1),
)
),
authors = listOf(
AuthorEntity(
id = 2,
name = "name",
imageUrl = "imageUrl"
)
),
)
val episode = populatedEpisode.asExternalModel()
assertEquals(
Episode(
id = 0,
name = "Test",
publishDate = Instant.fromEpochMilliseconds(1),
alternateAudio = "audio",
alternateVideo = "video",
newsResources = listOf(
NewsResource(
id = 1,
episodeId = 0,
title = "news",
content = "Hilt",
url = "url",
type = Video,
publishDate = Instant.fromEpochMilliseconds(1),
authors = listOf(),
topics = listOf()
)
),
authors = listOf(
Author(
id = 2,
name = "name",
imageUrl = "imageUrl"
)
),
),
episode
)
}
}

@ -0,0 +1,93 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.datetime.Instant
import org.junit.Assert.assertEquals
import org.junit.Test
class PopulatedNewsResourceKtTest {
@Test
fun populated_news_resource_can_be_mapped_to_news_resource() {
val populatedNewsResource = PopulatedNewsResource(
entity = NewsResourceEntity(
id = 1,
episodeId = 0,
title = "news",
content = "Hilt",
url = "url",
type = Video,
publishDate = Instant.fromEpochMilliseconds(1),
),
episode = EpisodeEntity(
id = 4,
name = "episode 4",
publishDate = Instant.fromEpochMilliseconds(2),
alternateAudio = "audio",
alternateVideo = "video",
),
authors = listOf(
AuthorEntity(
id = 2,
name = "name",
imageUrl = "imageUrl"
)
),
topics = listOf(
TopicEntity(
id = 3,
name = "name",
description = "description",
followed = true,
)
),
)
val newsResource = populatedNewsResource.asExternalModel()
assertEquals(
NewsResource(
id = 1,
episodeId = 0,
title = "news",
content = "Hilt",
url = "url",
type = Video,
publishDate = Instant.fromEpochMilliseconds(1),
authors = listOf(
Author(
id = 2,
name = "name",
imageUrl = "imageUrl"
)
),
topics = listOf(
Topic(
id = 3,
name = "name",
description = "description",
followed = true,
)
)
),
newsResource
)
}
}

@ -14,16 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model package com.google.samples.apps.nowinandroid.core.domain.model
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Article
import com.google.samples.apps.nowinandroid.core.model.network.NetworkAuthor import com.google.samples.apps.nowinandroid.core.network.model.NetworkAuthor
import com.google.samples.apps.nowinandroid.core.model.network.NetworkEpisode import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisode
import com.google.samples.apps.nowinandroid.core.model.network.NetworkEpisodeExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkEpisodeExpanded
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResourceExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import com.google.samples.apps.nowinandroid.core.model.network.asEntity
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
@ -60,15 +59,16 @@ class NetworkEntityKtTest {
@Test @Test
fun network_news_resource_can_be_mapped_to_news_resource_entity() { fun network_news_resource_can_be_mapped_to_news_resource_entity() {
val networkModel = NetworkNewsResource( val networkModel =
id = 0, NetworkNewsResource(
episodeId = 2, id = 0,
title = "title", episodeId = 2,
content = "content", title = "title",
url = "url", content = "content",
publishDate = Instant.fromEpochMilliseconds(1), url = "url",
type = NewsResourceType.Article.displayText, publishDate = Instant.fromEpochMilliseconds(1),
) type = Article,
)
val entity = networkModel.asEntity() val entity = networkModel.asEntity()
assertEquals(0, entity.id) assertEquals(0, entity.id)
@ -77,17 +77,18 @@ class NetworkEntityKtTest {
assertEquals("content", entity.content) assertEquals("content", entity.content)
assertEquals("url", entity.url) assertEquals("url", entity.url)
assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate) assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate)
assertEquals(NewsResourceType.Article.displayText, entity.type) assertEquals(Article, entity.type)
val expandedNetworkModel = NetworkNewsResourceExpanded( val expandedNetworkModel =
id = 0, NetworkNewsResourceExpanded(
episodeId = 2, id = 0,
title = "title", episodeId = 2,
content = "content", title = "title",
url = "url", content = "content",
publishDate = Instant.fromEpochMilliseconds(1), url = "url",
type = NewsResourceType.Article.displayText, publishDate = Instant.fromEpochMilliseconds(1),
) type = Article,
)
val entityFromExpanded = expandedNetworkModel.asEntity() val entityFromExpanded = expandedNetworkModel.asEntity()
@ -97,7 +98,7 @@ class NetworkEntityKtTest {
assertEquals("content", entityFromExpanded.content) assertEquals("content", entityFromExpanded.content)
assertEquals("url", entityFromExpanded.url) assertEquals("url", entityFromExpanded.url)
assertEquals(Instant.fromEpochMilliseconds(1), entityFromExpanded.publishDate) assertEquals(Instant.fromEpochMilliseconds(1), entityFromExpanded.publishDate)
assertEquals(NewsResourceType.Article.displayText, entityFromExpanded.type) assertEquals(Article, entityFromExpanded.type)
} }
@Test @Test
@ -117,13 +118,14 @@ class NetworkEntityKtTest {
assertEquals("alternateAudio", entity.alternateAudio) assertEquals("alternateAudio", entity.alternateAudio)
assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate) assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate)
val expandedNetworkModel = NetworkEpisodeExpanded( val expandedNetworkModel =
id = 0, NetworkEpisodeExpanded(
name = "name", id = 0,
publishDate = Instant.fromEpochMilliseconds(1), name = "name",
alternateVideo = "alternateVideo", publishDate = Instant.fromEpochMilliseconds(1),
alternateAudio = "alternateAudio", alternateVideo = "alternateVideo",
) alternateAudio = "alternateAudio",
)
val entityFromExpanded = expandedNetworkModel.asEntity() val entityFromExpanded = expandedNetworkModel.asEntity()

@ -0,0 +1,26 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.model.data
/**
* External data layer representation of an NiA Author
*/
data class Author(
val id: Int,
val name: String,
val imageUrl: String,
)

@ -16,33 +16,17 @@
package com.google.samples.apps.nowinandroid.core.model.data package com.google.samples.apps.nowinandroid.core.model.data
import androidx.room.Embedded import kotlinx.datetime.Instant
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
/** /**
* External data layer representation of an NiA episode * External data layer representation of an NiA episode
*/ */
data class Episode( data class Episode(
@Embedded val id: Int,
val entity: EpisodeEntity, val name: String,
@Relation( val publishDate: Instant,
parentColumn = "id", val alternateVideo: String?,
entityColumn = "episode_id" val alternateAudio: String?,
) val newsResources: List<NewsResource>,
val newsResources: List<NewsResourceEntity>, val authors: List<Author>
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = EpisodeAuthorCrossRef::class,
parentColumn = "episode_id",
entityColumn = "author_id",
)
)
val authors: List<AuthorEntity>
) )

@ -16,45 +16,19 @@
package com.google.samples.apps.nowinandroid.core.model.data package com.google.samples.apps.nowinandroid.core.model.data
import androidx.room.Embedded import kotlinx.datetime.Instant
import androidx.room.Junction
import androidx.room.Relation
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceAuthorCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceTopicCrossRef
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
/** /**
* External data layer representation of a fully populated NiA news resource * External data layer representation of a fully populated NiA news resource
*/ */
data class NewsResource( data class NewsResource(
@Embedded val id: Int,
val entity: NewsResourceEntity, val episodeId: Int,
@Relation( val title: String,
parentColumn = "episode_id", val content: String,
entityColumn = "id" val url: String,
) val publishDate: Instant,
val episode: EpisodeEntity, val type: NewsResourceType,
@Relation( val authors: List<Author>,
parentColumn = "id", val topics: List<Topic>
entityColumn = "id",
associateBy = Junction(
value = NewsResourceAuthorCrossRef::class,
parentColumn = "news_resource_id",
entityColumn = "author_id",
)
)
val authors: List<AuthorEntity>,
@Relation(
parentColumn = "id",
entityColumn = "id",
associateBy = Junction(
value = NewsResourceTopicCrossRef::class,
parentColumn = "news_resource_id",
entityColumn = "topic_id",
)
)
val topics: List<TopicEntity>
) )

@ -20,44 +20,61 @@ package com.google.samples.apps.nowinandroid.core.model.data
* Type for [NewsResource] * Type for [NewsResource]
*/ */
enum class NewsResourceType( enum class NewsResourceType(
val serializedName: String,
val displayText: String, val displayText: String,
// TODO: descriptions should probably be string resources // TODO: descriptions should probably be string resources
val description: String val description: String
) { ) {
Video( Video(
serializedName = "Video 📺",
displayText = "Video 📺", displayText = "Video 📺",
description = "A video published on YouTube" description = "A video published on YouTube"
), ),
APIChange( APIChange(
serializedName = "API change",
displayText = "API change", displayText = "API change",
description = "An addition, deprecation or change to the Android platform APIs." description = "An addition, deprecation or change to the Android platform APIs."
), ),
Article( Article(
serializedName = "Article 📚",
displayText = "Article 📚", displayText = "Article 📚",
description = "An article, typically on Medium or the official Android blog" description = "An article, typically on Medium or the official Android blog"
), ),
Codelab( Codelab(
serializedName = "Codelab",
displayText = "Codelab", displayText = "Codelab",
description = "A new or updated codelab" description = "A new or updated codelab"
), ),
Podcast( Podcast(
serializedName = "Podcast 🎙",
displayText = "Podcast 🎙", displayText = "Podcast 🎙",
description = "A podcast" description = "A podcast"
), ),
Docs( Docs(
serializedName = "Docs 📑",
displayText = "Docs 📑", displayText = "Docs 📑",
description = "A new or updated piece of documentation" description = "A new or updated piece of documentation"
), ),
Event( Event(
serializedName = "Event 📆",
displayText = "Event 📆", displayText = "Event 📆",
description = "Information about a developer event e.g. Android Developer Summit" description = "Information about a developer event e.g. Android Developer Summit"
), ),
DAC( DAC(
serializedName = "DAC",
displayText = "DAC", displayText = "DAC",
description = "Android version features - Information about features in an Android" description = "Android version features - Information about features in an Android"
), ),
Unknown( Unknown(
serializedName = "Unknown",
displayText = "Unknown", displayText = "Unknown",
description = "Unknown" description = "Unknown"
) )
} }
fun String?.asNewsResourceType() = when (this) {
null -> NewsResourceType.Unknown
else -> NewsResourceType.values()
.firstOrNull { type -> type.serializedName == this }
?: NewsResourceType.Unknown
}

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.network package com.google.samples.apps.nowinandroid.core.network
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
/** /**
* Interface representing network calls to the NIA backend * Interface representing network calls to the NIA backend

@ -16,8 +16,9 @@
package com.google.samples.apps.nowinandroid.core.network.fake package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant
@ -45,7 +46,7 @@ object FakeDataSource {
second = 0, second = 0,
nanosecond = 0 nanosecond = 0
).toInstant(TimeZone.UTC), ).toInstant(TimeZone.UTC),
type = "Video \uD83D\uDCFA", type = Video,
topics = listOf(0), topics = listOf(0),
) )

@ -16,10 +16,10 @@
package com.google.samples.apps.nowinandroid.core.network.fake package com.google.samples.apps.nowinandroid.core.network.fake
import com.google.samples.apps.nowinandroid.core.model.network.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.model.network.NetworkTopic
import com.google.samples.apps.nowinandroid.core.network.NiANetwork import com.google.samples.apps.nowinandroid.core.network.NiANetwork
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.network package com.google.samples.apps.nowinandroid.core.network.model
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity import com.google.samples.apps.nowinandroid.core.model.data.Author
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**
* Network representation of [AuthorEntity] * Network representation of [Author]
*/ */
@Serializable @Serializable
data class NetworkAuthor( data class NetworkAuthor(
@ -28,9 +28,3 @@ data class NetworkAuthor(
val name: String, val name: String,
val imageUrl: String, val imageUrl: String,
) )
fun NetworkAuthor.asEntity() = AuthorEntity(
id = id,
name = name,
imageUrl = imageUrl
)

@ -14,19 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.network package com.google.samples.apps.nowinandroid.core.network.model
import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.Episode
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity import com.google.samples.apps.nowinandroid.core.network.model.util.InstantSerializer
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**
* Network representation of [EpisodeEntity] when fetched from /networkepisodes * Network representation of [Episode] when fetched from /episodes
*/ */
@Serializable @Serializable
data class NetworkEpisode( data class NetworkEpisode(
@PrimaryKey
val id: Int, val id: Int,
val name: String, val name: String,
@Serializable(InstantSerializer::class) @Serializable(InstantSerializer::class)
@ -38,11 +37,10 @@ data class NetworkEpisode(
) )
/** /**
* Network representation of [EpisodeEntity] when fetched from /networkepisodes{id} * Network representation of [Episode] when fetched from /episodes/{id}
*/ */
@Serializable @Serializable
data class NetworkEpisodeExpanded( data class NetworkEpisodeExpanded(
@PrimaryKey
val id: Int, val id: Int,
val name: String, val name: String,
@Serializable(InstantSerializer::class) @Serializable(InstantSerializer::class)
@ -52,19 +50,3 @@ data class NetworkEpisodeExpanded(
val newsResources: List<NetworkNewsResource> = listOf(), val newsResources: List<NetworkNewsResource> = listOf(),
val authors: List<NetworkAuthor> = listOf(), val authors: List<NetworkAuthor> = listOf(),
) )
fun NetworkEpisode.asEntity() = EpisodeEntity(
id = id,
name = name,
publishDate = publishDate,
alternateVideo = alternateVideo,
alternateAudio = alternateAudio,
)
fun NetworkEpisodeExpanded.asEntity() = EpisodeEntity(
id = id,
name = name,
publishDate = publishDate,
alternateVideo = alternateVideo,
alternateAudio = alternateAudio,
)

@ -14,14 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.network package com.google.samples.apps.nowinandroid.core.network.model
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.network.model.util.InstantSerializer
import com.google.samples.apps.nowinandroid.core.network.model.util.NewsResourceTypeSerializer
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**
* Network representation of [NewsResourceEntity] when fetched from /networkresources * Network representation of [NewsResource] when fetched from /newsresources
*/ */
@Serializable @Serializable
data class NetworkNewsResource( data class NetworkNewsResource(
@ -32,13 +35,14 @@ data class NetworkNewsResource(
val url: String, val url: String,
@Serializable(InstantSerializer::class) @Serializable(InstantSerializer::class)
val publishDate: Instant, val publishDate: Instant,
val type: String, @Serializable(NewsResourceTypeSerializer::class)
val type: NewsResourceType,
val authors: List<Int> = listOf(), val authors: List<Int> = listOf(),
val topics: List<Int> = listOf(), val topics: List<Int> = listOf(),
) )
/** /**
* Network representation of [NewsResourceEntity] when fetched from /networkresources{id} * Network representation of [NewsResource] when fetched from /newsresources/{id}
*/ */
@Serializable @Serializable
data class NetworkNewsResourceExpanded( data class NetworkNewsResourceExpanded(
@ -49,27 +53,8 @@ data class NetworkNewsResourceExpanded(
val url: String, val url: String,
@Serializable(InstantSerializer::class) @Serializable(InstantSerializer::class)
val publishDate: Instant, val publishDate: Instant,
val type: String, @Serializable(NewsResourceTypeSerializer::class)
val type: NewsResourceType,
val authors: List<NetworkAuthor> = listOf(), val authors: List<NetworkAuthor> = listOf(),
val topics: List<NetworkTopic> = listOf(), val topics: List<NetworkTopic> = listOf(),
) )
fun NetworkNewsResource.asEntity() = NewsResourceEntity(
id = id,
episodeId = episodeId,
title = title,
content = content,
url = url,
publishDate = publishDate,
type = type,
)
fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity(
id = id,
episodeId = episodeId,
title = title,
content = content,
url = url,
publishDate = publishDate,
type = type,
)

@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.network package com.google.samples.apps.nowinandroid.core.network.model
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity import com.google.samples.apps.nowinandroid.core.model.data.Topic
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**
* Network representation of [TopicEntity] * Network representation of [Topic]
*/ */
@Serializable @Serializable
data class NetworkTopic( data class NetworkTopic(
@ -29,10 +29,3 @@ data class NetworkTopic(
val description: String = "", val description: String = "",
val followed: Boolean = false, val followed: Boolean = false,
) )
fun NetworkTopic.asEntity() = TopicEntity(
id = id,
name = name,
description = description,
followed = followed
)

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.model.network package com.google.samples.apps.nowinandroid.core.network.model.util
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant

@ -0,0 +1,39 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.network.model.util
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
import com.google.samples.apps.nowinandroid.core.model.data.asNewsResourceType
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind.STRING
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
object NewsResourceTypeSerializer : KSerializer<NewsResourceType> {
override fun deserialize(decoder: Decoder): NewsResourceType =
decoder.decodeString().asNewsResourceType()
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(
serialName = "type",
kind = STRING
)
override fun serialize(encoder: Encoder, value: NewsResourceType) =
encoder.encodeString(value.name)
}

@ -37,11 +37,10 @@ import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.model.entities.AuthorEntity import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Article
import com.google.samples.apps.nowinandroid.core.model.entities.EpisodeEntity import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.entities.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.model.entities.TopicEntity
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
@ -59,12 +58,12 @@ fun NewsResourceCardExpanded(
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
) { ) {
Row { Row {
NewsResourceTitle(newsResource.entity.title, modifier = Modifier.fillMaxWidth((.8f))) NewsResourceTitle(newsResource.title, modifier = Modifier.fillMaxWidth((.8f)))
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
BookmarkButton(isBookmarked, onToggleBookmark) BookmarkButton(isBookmarked, onToggleBookmark)
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
NewsResourceShortDescription(newsResource.entity.content) NewsResourceShortDescription(newsResource.content)
} }
} }
@ -174,31 +173,22 @@ fun ExpandedNewsResourcePreview() {
} }
private val newsResource = NewsResource( private val newsResource = NewsResource(
NewsResourceEntity( id = 1,
id = 1, episodeId = 1,
episodeId = 1, title = "Title",
title = "Title", content = "Content",
content = "Content", url = "url",
url = "url", publishDate = Instant.DISTANT_FUTURE,
publishDate = Instant.DISTANT_FUTURE, type = Article,
type = "type", authors = listOf(
), Author(
EpisodeEntity(
id = 1,
name = "Title",
publishDate = Instant.DISTANT_FUTURE,
alternateVideo = "alternateVideo",
alternateAudio = "alternateAudio",
),
listOf(
AuthorEntity(
id = 1, id = 1,
name = "Name", name = "Name",
imageUrl = "imageUrl" imageUrl = "imageUrl"
) )
), ),
listOf( topics = listOf(
TopicEntity( Topic(
id = 1, id = 1,
name = "Name", name = "Name",
description = "Description", description = "Description",

@ -145,6 +145,7 @@ private val testOutputTopics = listOf(
Topic( Topic(
id = 2, id = 2,
name = TOPIC_3_NAME, name = TOPIC_3_NAME,
description = TOPIC_DESC description = TOPIC_DESC,
followed = false,
) )
) )

@ -109,7 +109,7 @@ class ForYouViewModelTest {
Topic( Topic(
id = 2, id = 2,
name = "Tools", name = "Tools",
description = "" description = "",
) to false ) to false
), ),
feed = emptyList() feed = emptyList()

Loading…
Cancel
Save