diff --git a/app/src/main/graphql/fragments.graphql b/app/src/main/graphql/fragments.graphql index 748d88c595..ae66c47689 100644 --- a/app/src/main/graphql/fragments.graphql +++ b/app/src/main/graphql/fragments.graphql @@ -292,6 +292,17 @@ fragment project on Project { slug } +fragment similarProject on Project { + id + pid + name + slug + isLaunched + deadlineAt + percentFunded + imageUrl(width: $imageUrlWidth) +} + fragment reward on Reward { id name diff --git a/app/src/main/graphql/project.graphql b/app/src/main/graphql/project.graphql index cce9139fe8..d387908378 100644 --- a/app/src/main/graphql/project.graphql +++ b/app/src/main/graphql/project.graphql @@ -214,4 +214,19 @@ query BuildPaymentPlan($slug: String!, $amount: String!) { } } } +} + +query FetchSimilarProjects($first: Int = 4, $similarToPid: String!, $excludePid: Int!, $recommended: Boolean, $seed: Int, $imageUrlWidth: Int = 1024) { + projects( + first: $first + state: LIVE + similarToPid: $similarToPid + excludePids: [$excludePid] + seed: $seed + recommended: $recommended + ) { + nodes { + ...similarProject + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/kickstarter/mock/services/MockApolloClientV2.kt b/app/src/main/java/com/kickstarter/mock/services/MockApolloClientV2.kt index 6781d5d66a..dde5f22bab 100644 --- a/app/src/main/java/com/kickstarter/mock/services/MockApolloClientV2.kt +++ b/app/src/main/java/com/kickstarter/mock/services/MockApolloClientV2.kt @@ -358,6 +358,10 @@ open class MockApolloClientV2 : ApolloClientTypeV2 { return Result.success(SearchEnvelope()) } + override suspend fun fetchSimilarProjects(pid: Long): Result> { + return Result.success(listOf()) + } + override fun cleanDisposables() { TODO("Not yet implemented") } diff --git a/app/src/main/java/com/kickstarter/models/Project.kt b/app/src/main/java/com/kickstarter/models/Project.kt index 7ead58cb00..b4c891d3ec 100644 --- a/app/src/main/java/com/kickstarter/models/Project.kt +++ b/app/src/main/java/com/kickstarter/models/Project.kt @@ -40,6 +40,7 @@ class Project private constructor( private val launchedAt: DateTime?, private val location: Location?, private val name: String, + private val percentFunded: Int?, private val permissions: List?, private val pledged: Double, private val photo: Photo?, @@ -99,6 +100,7 @@ class Project private constructor( fun launchedAt() = this.launchedAt fun location() = this.location fun name() = this.name + fun percentFunded() = this.percentFunded fun permissions() = this.permissions fun pledged() = this.pledged fun photo() = this.photo @@ -161,6 +163,7 @@ class Project private constructor( private var launchedAt: DateTime? = null, private var location: Location? = null, private var name: String = "", + private var percentFunded: Int? = null, private var permissions: List? = null, private var pledged: Double = 0.0, private var photo: Photo? = null, @@ -225,6 +228,7 @@ class Project private constructor( fun launchedAt(launchedAt: DateTime?) = apply { this.launchedAt = launchedAt } fun location(location: Location?) = apply { this.location = location } fun name(name: String?) = apply { this.name = name ?: "" } + fun percentFunded(percentFunded: Int?) = apply { this.percentFunded = percentFunded } fun permissions(permissions: List?) = apply { this.permissions = permissions?.filterNotNull() ?: emptyList() } fun pledged(pledged: Double?) = apply { this.pledged = pledged ?: 0.0 } fun photo(photo: Photo?) = apply { this.photo = photo } @@ -282,6 +286,7 @@ class Project private constructor( launchedAt = launchedAt, location = location, name = name, + percentFunded = percentFunded, permissions = permissions, pledged = pledged, photo = photo, @@ -345,6 +350,7 @@ class Project private constructor( launchedAt = launchedAt, location = location, name = name, + percentFunded = percentFunded, permissions = permissions, pledged = pledged, photo = photo, @@ -521,6 +527,7 @@ class Project private constructor( launchedAt() == other.launchedAt() && location() == other.location() && name() == other.name() && + percentFunded() == other.percentFunded() && permissions() == other.permissions() && pledged() == other.pledged() && photo() == other.photo() && diff --git a/app/src/main/java/com/kickstarter/services/KSApolloClientV2.kt b/app/src/main/java/com/kickstarter/services/KSApolloClientV2.kt index 42df91bb83..406e39ac02 100644 --- a/app/src/main/java/com/kickstarter/services/KSApolloClientV2.kt +++ b/app/src/main/java/com/kickstarter/services/KSApolloClientV2.kt @@ -31,6 +31,7 @@ import com.kickstarter.FetchCategoryQuery import com.kickstarter.FetchProjectQuery import com.kickstarter.FetchProjectRewardsQuery import com.kickstarter.FetchProjectsQuery +import com.kickstarter.FetchSimilarProjectsQuery import com.kickstarter.GetBackingQuery import com.kickstarter.GetCommentQuery import com.kickstarter.GetProjectAddOnsQuery @@ -228,8 +229,9 @@ interface ApolloClientTypeV2 { fun getPledgedProjectsOverviewPledges(inputData: PledgedProjectsOverviewQueryData): Observable fun getRewardsFromProject(slug: String): Observable> fun buildPaymentPlan(input: BuildPaymentPlanData): Observable - suspend fun getSearchProjects(discoveryParams: DiscoveryParams, cursor: String? = null): Result fun updateBackerCompleted(inputData: UpdateBackerCompletedData): Observable + suspend fun getSearchProjects(discoveryParams: DiscoveryParams, cursor: String? = null): Result + suspend fun fetchSimilarProjects(pid: Long): Result> fun cleanDisposables() } @@ -1847,6 +1849,26 @@ class KSApolloClientV2(val service: ApolloClient, val gson: Gson) : ApolloClient } ?: SearchEnvelope() } + override suspend fun fetchSimilarProjects(pid: Long): Result> = executeForResult { + val query = FetchSimilarProjectsQuery( + first = Optional.present(4), + similarToPid = pid.toString(), + excludePid = pid.toInt(), + recommended = Optional.present(true), + seed = Optional.present(pid.toInt()) + ) + val response = this.service.query(query).execute() + + if (response.hasErrors()) + throw buildClientException(response.errors) + + response.data?.let { responseData -> + responseData.projects?.nodes?.map { + projectTransformer(it?.similarProject) + } + } ?: emptyList() + } + sealed class KSApolloClientV2Exception( message: String? = null, cause: Throwable? = null diff --git a/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt b/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt index 0eae50978a..06989afdd1 100644 --- a/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt +++ b/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt @@ -21,6 +21,7 @@ import com.kickstarter.fragment.FullProject import com.kickstarter.fragment.PpoCard.DeliveryAddress import com.kickstarter.fragment.ProjectCard import com.kickstarter.fragment.RewardImage +import com.kickstarter.fragment.SimilarProject import com.kickstarter.libs.Permission import com.kickstarter.libs.utils.extensions.isNotNull import com.kickstarter.libs.utils.extensions.isNull @@ -833,6 +834,32 @@ fun backingTransformer(backingGr: com.kickstarter.fragment.Backing?): Backing { .build() } +/** + * Transform the Project GraphQL data structure into our own Project data model + * @param fragment.SimilarProject similarProjectFragment + * @return Project + */ +fun projectTransformer(similarProjectFragment: SimilarProject?): Project { + val id = decodeRelayId(similarProjectFragment?.id) ?: -1 + val name = similarProjectFragment?.name + val slug = similarProjectFragment?.slug + val displayPrelaunch = (similarProjectFragment?.isLaunched ?: false).negate() + val deadline = similarProjectFragment?.deadlineAt + val percentFunded = similarProjectFragment?.percentFunded + val photo = getPhoto(similarProjectFragment?.imageUrl, null) + + return Project.builder() + .currencyTrailingCode(false) // - This field is available on V1 Configuration Object + .displayPrelaunch(displayPrelaunch) + .deadline(deadline) + .id(id) + .name(name) + .percentFunded(percentFunded) + .photo(photo) // - now we get the full size for field from GraphQL, but V1 provided several image sizes + .slug(slug) + .build() +} + /** * For addOns we receive this kind of data structure :[D, D, D, D, D, C, E, E] * and we need to transform it in : D(5),C(1),E(2)