From 3edb8661aef9d01715adc4aac2b622a39bab5722 Mon Sep 17 00:00:00 2001 From: Leigh Douglas Date: Mon, 26 Aug 2024 13:13:55 -0400 Subject: [PATCH] MBL-1659: Update card tags/pills to match backend schema and other small changes (#2109) * update string typo * update flags, remove old flags, create string ext method * lint * remove typo * make flags nullable * fix and add tests * Remove unused tag --------- Co-authored-by: Leigh Douglas --- .../graphql/pledgeProjectsOverview.graphql | 5 + app/src/main/graphql/schema.graphqls | 430 +++++++++++------- .../pledgedprojectsoverview/data/Flag.kt | 52 +++ .../pledgedprojectsoverview/data/PPOCard.kt | 79 ++-- .../pledgedprojectsoverview/ui/PPOCardView.kt | 139 ++---- .../ui/PledgedProjectsOverviewScreen.kt | 5 +- .../libs/utils/extensions/StringExt.kt | 37 ++ .../transformers/GraphQLTransformers.kt | 5 + .../ui/compose/designsystem/KSBadges.kt | 98 ++++ .../ui/compose/designsystem/KSColors.kt | 3 + app/src/main/res/values/strings.xml | 4 +- .../activities/compose/PPOCardViewKtTest.kt | 78 +++- 12 files changed, 597 insertions(+), 338 deletions(-) create mode 100644 app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/Flag.kt diff --git a/app/src/main/graphql/pledgeProjectsOverview.graphql b/app/src/main/graphql/pledgeProjectsOverview.graphql index b0f7eda79c..7f1fed6c37 100644 --- a/app/src/main/graphql/pledgeProjectsOverview.graphql +++ b/app/src/main/graphql/pledgeProjectsOverview.graphql @@ -9,6 +9,11 @@ query PledgedProjectsOverview($first: Int, $after: String, $last: Int, $before: ...ppoCard } tierType + flags { + icon + message + type + } } } pageInfo { diff --git a/app/src/main/graphql/schema.graphqls b/app/src/main/graphql/schema.graphqls index 37baf36f94..126c11a8d1 100644 --- a/app/src/main/graphql/schema.graphqls +++ b/app/src/main/graphql/schema.graphqls @@ -549,6 +549,7 @@ enum Feature { address_collection_for_digital_rewards_2024 prelaunch_story_editor prelaunch_story_exception + creator_nav_refresh } """ @@ -852,6 +853,10 @@ type Project implements Node & Commentable { """ actions: ProjectActions! """ + The active checkout_wave, if there is one + """ + activeWave: CheckoutWave + """ Backing Add-ons """ addOns("""Returns the first _n_ elements from the list.""" first: Int, """Returns the elements in the list that come after the specified cursor.""" after: String, """Returns the last _n_ elements from the list.""" last: Int, """Returns the elements in the list that come before the specified cursor.""" before: String, """Filters available add ons by given location""" forLocation: ID, """Enables/disables add-ons sort by cost and title, with sorting enabled by default""" sort: Boolean): ProjectRewardConnection @@ -905,10 +910,6 @@ type Project implements Node & Commentable { """ backing: Backing """ - Business addresses associated with the project - includes the main address and seconday addresses - """ - businessAddresses("""Returns the first _n_ elements from the list.""" first: Int, """Returns the elements in the list that come after the specified cursor.""" after: String, """Returns the last _n_ elements from the list.""" last: Int, """Returns the elements in the list that come before the specified cursor.""" before: String): ProjectBusinessAddressConnection - """ True if the current user can comment (considers restrictions) """ canComment: Boolean! @@ -1081,10 +1082,6 @@ type Project implements Node & Commentable { """ googleAnalyticsTrackingId: String """ - Whether or not the project has at least one item-level question or option - """ - hasItemQuestionsOrOptions: Boolean! - """ Does this project have any post-campaign config? """ hasPostCampaignConfig: Boolean! @@ -1100,10 +1097,6 @@ type Project implements Node & Commentable { Whether or not the project has supporting materials (Prototype Gallery) """ hasSupportingMaterials: Boolean! - """ - Whether or not the project has at least one item-level question, item-level option selection, or project-level question - """ - hasSurveyQuestionsOrSelections: Boolean! id: ID! """ The project's primary image. @@ -1114,6 +1107,10 @@ type Project implements Node & Commentable { """ imageUrl("""Whether or not to blur the image.""" blur: Boolean, """The width of the image in pixels.""" width: Int!): String! """ + Is the project's submission state accepted. + """ + isApproved: Boolean! + """ Has the current user disliked this project? """ isDisliked: Boolean! @@ -1339,6 +1336,10 @@ type Project implements Node & Commentable { """ statsInterval: Int """ + Locations from which the project fulfills + """ + stockLocations: [StockLocation!]! + """ The story behind the project, parsed for presentation. """ story("""The number of visible characters to fetch (does not include HTML markup).""" first: Int, """The width of embedded assets in pixels.""" assetWidth: Int): HTML! @@ -1371,6 +1372,10 @@ type Project implements Node & Commentable { """ targetLaunchDateUpdatedAt: ISO8601DateTime """ + Tax categories configured for the project + """ + taxCategories: [TaxCategory!]! + """ The timeline of project events, including updates and milestones. """ timeline("""Returns the first _n_ elements from the list.""" first: Int, """Returns the elements in the list that come after the specified cursor.""" after: String, """Returns the last _n_ elements from the list.""" last: Int, """Returns the elements in the list that come before the specified cursor.""" before: String, """Makes any pinned post the first item in the timeline""" withPinnedFirst: Boolean): ProjectTimelineConnection @@ -1609,6 +1614,10 @@ type Backing implements Node { """ backingDetailsPageUrl: String! """ + The backing rewards on a backing + """ + backingRewards: [BackingAddon!] + """ A link to the backing information """ backingUrl: String @@ -2100,9 +2109,13 @@ A cart associated with a backing """ type Cart { """ - The answers to project-level survey questions + Answers associated with a cart's backing rewards """ - answers: [Answer!]! + backingRewardAnswers: [Answer!]! + """ + The answers to reward-level questions + """ + baseRewardAnswers: [Answer!]! """ When the cart was finalized (i.e., when the backer submitted responses) """ @@ -2113,6 +2126,10 @@ type Cart { """ lineItems: [LineItem!]! """ + The answers to project-level questions + """ + projectAnswers: [Answer!]! + """ Whether or not this cart needs an address to be finalized """ shouldCollectAddress: Boolean! @@ -2247,6 +2264,10 @@ type RewardItem implements Node { Tax related configuration """ taxConfig: ItemTaxConfig + """ + Whether the item is completely configured for tax purposes + """ + taxConfigCompletionStatus: PrereqCompletionStatus! } """ @@ -2402,6 +2423,10 @@ Inclusion in this list does not necessarily indicate that the reward is availabl """ shippingRates: [ShippingRate!]! """ + Whether the shipping rates are filled out and not missing fields + """ + shippingRatesValid: PrereqCompletionStatus! + """ Shipping rules defined by the creator for this reward """ shippingRules: [ShippingRule]! @@ -2581,6 +2606,10 @@ enum ShippingPreference { A project reward's shipping rule. """ type ShippingRule implements Node { + """ + Number of backers for the Shipping Rule + """ + backersCount: Int! """ The shipping cost for this location. """ @@ -2674,6 +2703,24 @@ Types that can have shipping rates """ union Shippable = Reward | RewardItem +""" +Completion status of objects necessary for pledge redemption +""" +enum PrereqCompletionStatus { + """ + Prereq has not been started + """ + not_started + """ + Prereq has been started but is not complete + """ + incomplete + """ + Prereq is complete + """ + complete +} + """ Whether a reward contains all physical goods, some, none, or belongs to a legacy project. """ @@ -2702,6 +2749,10 @@ type Question { """ optional: Boolean! """ + Position of a question + """ + position: Int! + """ The question prompt """ prompt: String! @@ -2734,116 +2785,25 @@ Tax configuration data related to an item type ItemTaxConfig { id: ID! """ - The associated item - """ - item: RewardItem - """ - Item type, e.g. physical, digital, event, etc - """ - itemType: ItemTypeEnum - """ - Where will the item be picked up or where is the event? - """ - localAddress: BusinessAddress - """ Value of item pre any bundling discounts """ marketValue: Money - """ - Where will the item ship from? - """ - shipFromAddress: BusinessAddress - """ - Item shipping preference - """ - shippingPreference: TaxConfigShippingPreference - """ - Third party tax code - """ - taxCode: String + taxCategory: TaxCategory } """ -Item types related to tax concerns, e.g. is the item physical, tax-exempt, an event, etc +A project scoped tax category """ -enum ItemTypeEnum { - """ - digital - """ - digital - """ - physical - """ - physical - """ - experience_event_service - """ - experience_event_service - """ - tax_exempt - """ - tax_exempt -} - -""" -Item level shipping preferences, e.g. shipping, local, shipping_and_local, none -""" -enum TaxConfigShippingPreference { - """ - shipping - """ - shipping - """ - local - """ - local - """ - shipping_and_local - """ - shipping_and_local - """ - none - """ - none -} - -""" -A business address. -""" -type BusinessAddress implements Node { - """ - The first address line - """ - addressLine1: String! - """ - The second address line - """ - addressLine2: String - """ - The address city - """ - city: String - """ - The address contact name - """ - contactName: String - """ - The address country, e.g. US - """ - country: String! +type TaxCategory { id: ID! """ - Items associated with the address. - """ - items: [RewardItem!] + Third party tax code """ - The address postal_code + taxCode: String! """ - postalCode: String! + The user assigned label for the vendor tax code """ - The address region, e.g. North Dakota - """ - region: String + userLabel: String! } """ @@ -2879,6 +2839,10 @@ type BackingAddon { """ amount: Money! """ + The addon reward + """ + backerReward: Reward! + """ The add-on description. """ description: String! @@ -3308,7 +3272,7 @@ type RichTextQuote { """ A Photo asset -e""" +""" type RichTextPhoto { altText: String! asset: Photo @@ -3779,6 +3743,10 @@ type Category implements Node { """ parentId: ID """ + The translatable phrase (e.g. "An art project")) + """ + projectPhrase: String + """ Projects in a category. """ projects("""Returns the first _n_ elements from the list.""" first: Int, """Returns the elements in the list that come after the specified cursor.""" after: String, """Returns the last _n_ elements from the list.""" last: Int, """Returns the elements in the list that come before the specified cursor.""" before: String, """Filter projects by publically accessible state.""" state: PublicProjectState!): CategoryProjectsConnection @@ -5855,6 +5823,14 @@ type BackerSurvey { The number of backers who have not answered the survey """ backersRemaining: Int! + """ + Whether the survey has at least one question or option + """ + hasBackerQuestionsOrItemOptions: Boolean! + """ + Whether the survey has at least one item-level question or option + """ + hasItemQuestionsOrOptions: Boolean! id: ID! """ When the survey was sent @@ -5867,40 +5843,35 @@ type BackerSurvey { } """ -The connection type for BusinessAddress. +Location from which a project fulfills """ -type ProjectBusinessAddressConnection { - """ - A list of edges. - """ - edges: [BusinessAddressEdge] - """ - A list of nodes. - """ - nodes: [BusinessAddress] - """ - Information to aid in pagination. - """ - pageInfo: PageInfo! - totalCount: Int! +type StockLocation { + address: Address! + id: ID! + project: Project! } """ -An edge in a connection. +Represents the window during which backers can redeem their backing """ -type BusinessAddressEdge { +type CheckoutWave { """ - A cursor for use in pagination. + Whether the wave is currently active """ - cursor: String! + active: Boolean! + """ + The end date of the wave """ - Whether or not this is the main address for the project + endsAt: DateTime! + id: ID! """ - mainAddress: Boolean! + The associated project """ - The item at the end of the edge. + project: Project! """ - node: BusinessAddress + The start date of the wave + """ + startsAt: DateTime! } """ @@ -8125,13 +8096,31 @@ type PledgeProjectOverviewItem { """ tags """ - tags: [String!] + flags: [PledgedProjectsOverviewPledgeFlags!] """ tier type """ tierType: String } +""" +Flags for pledge in projects overview dashboard +""" +type PledgedProjectsOverviewPledgeFlags { + """ + Flag icon type, e.g. time, alert, etc. + """ + icon: String + """ + Translated flag message + """ + message: String + """ + Flag type, e.g. warning, alert, etc. + """ + type: String +} + """ The mutation root of the Kickstarter GraphQL interface """ @@ -8161,7 +8150,7 @@ type Mutation { """ blockUser(input: BlockUserInput!): BlockUserPayload """ - Bulk edits the entire set of questions for a questionable + Bulk edits the entire set of backer questions for a backer survey """ bulkEditQuestions(input: BulkEditQuestionsInput!): BulkEditQuestionsPayload """ @@ -8190,7 +8179,7 @@ type Mutation { """ copyRewardItems(input: CopyRewardItemsInput!): CopyRewardItemsPayload """ - Save a new shipping address. + Save a new address. """ createAddress(input: CreateAddressInput!): CreateAddressPayload """ @@ -8247,7 +8236,7 @@ type Mutation { createOption(input: CreateOptionInput!): CreateOptionPayload createOrUpdateBackingAddress(input: CreateOrUpdateBackingAddressInput!): CreateOrUpdateBackingAddressPayload """ - Sets tax related info for an item + Creates/updates item tax config and creates/updates tax category for an item """ createOrUpdateItemTaxConfig(input: CreateOrUpdateItemTaxConfigInput!): CreateOrUpdateItemTaxConfigPayload """ @@ -8321,6 +8310,10 @@ type Mutation { """ createVideoTrack(input: CreateVideoTrackInput!): CreateVideoTrackPayload """ + Creates a checkout wave for a given project, assuming all pre-requisites are complete. + """ + createWave(input: CreateWaveInput!): CreateWavePayload + """ Deactivates the prelaunch page for a project """ deactivatePrelaunch(input: DeactivateProjectPrelaunchInput!): DeactivateProjectPrelaunchPayload @@ -8507,6 +8500,10 @@ type Mutation { """ setProjectStatus(input: SetProjectStatusInput!): SetProjectStatusPayload """ + Sets tax category for an item + """ + setTaxCategory(input: SetTaxCategoryInput!): SetTaxCategoryPayload + """ Signs in or sign up a user via the Sign in With Apple service """ signInWithApple(input: SignInWithAppleInput!): SignInWithApplePayload @@ -10270,6 +10267,10 @@ type CreateAddressPayload { """ assignedToBacking: Boolean! """ + Project was associated with address + """ + assignedToProject: Boolean! + """ A unique identifier for the client performing the mutation. """ clientMutationId: String @@ -10308,6 +10309,7 @@ input CreateAddressInput { phoneNumber: String! primary: Boolean backingId: ID + projectId: ID """ A unique identifier for the client performing the mutation. """ @@ -10359,6 +10361,7 @@ Autogenerated input type of AcceptOrRejectAddressSuggestion input AcceptOrRejectAddressSuggestionInput { fulfillmentAddressId: ID! suggestionAccepted: Boolean! + projectId: ID """ A unique identifier for the client performing the mutation. """ @@ -12467,34 +12470,6 @@ input CreateAttributionEventInput { clientMutationId: String } -""" -Autogenerated return type of CreateOrUpdateItemTaxConfig -""" -type CreateOrUpdateItemTaxConfigPayload { - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String - item: RewardItem! -} - -""" -Autogenerated input type of CreateOrUpdateItemTaxConfig -""" -input CreateOrUpdateItemTaxConfigInput { - itemId: String! - itemType: ItemTypeEnum - taxCode: String - marketValue: String - shippingPreference: TaxConfigShippingPreference - shipFromAddressId: String - localAddressId: String - """ - A unique identifier for the client performing the mutation. - """ - clientMutationId: String -} - """ Autogenerated return type of UpdateOrderState """ @@ -12605,6 +12580,111 @@ input ConfirmOrderAddressInput { clientMutationId: String } +""" +Autogenerated return type of CreateWave +""" +type CreateWavePayload { + checkoutWave: CheckoutWave! + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String +} + +""" +Autogenerated input type of CreateWave +""" +input CreateWaveInput { + """ + The project id + """ + projectId: ID! + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String +} + +""" +Autogenerated return type of SetTaxCategory +""" +type SetTaxCategoryPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + """ + The updated item tax config + """ + itemTaxConfig: ItemTaxConfig! +} + +""" +Autogenerated input type of SetTaxCategory +""" +input SetTaxCategoryInput { + """ + The item id + """ + itemId: ID! + """ + The tax category id + """ + taxCategoryId: ID + """ + Input for creating or updating a tax category + """ + taxCategoryInput: TaxCategoryInput + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String +} + +""" +Input to create or update a tax category +""" +input TaxCategoryInput { + userLabel: String! + taxCode: String! +} + +""" +Autogenerated return type of CreateOrUpdateItemTaxConfig +""" +type CreateOrUpdateItemTaxConfigPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + """ + The created or updated item tax config + """ + itemTaxConfig: ItemTaxConfig! +} + +""" +Autogenerated input type of CreateOrUpdateItemTaxConfig +""" +input CreateOrUpdateItemTaxConfigInput { + """ + The item id + """ + itemId: ID! + """ + If the item is taxable + """ + itemIsTaxExempt: Boolean! + """ + The market value of the item + """ + marketValue: Int + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String +} + """ Autogenerated return type of CreateOption """ @@ -12778,21 +12858,20 @@ Autogenerated return type of BulkEditQuestions """ type BulkEditQuestionsPayload { """ - A unique identifier for the client performing the mutation. + The updated backer survey. """ - clientMutationId: String + backerSurvey: BackerSurvey """ - The updated questionable. + A unique identifier for the client performing the mutation. """ - questionable: Questionable + clientMutationId: String } """ Autogenerated input type of BulkEditQuestions """ input BulkEditQuestionsInput { - questionableId: ID! - questionableType: QuestionableType! + projectId: ID! questions: [QuestionInput!]! """ A unique identifier for the client performing the mutation. @@ -12806,6 +12885,8 @@ Question associated with a particular questionable. input QuestionInput { type: QuestionType! prompt: String! + questionableId: String! + questionableType: QuestionableType! choices: [String!] choiceSelectionLimit: Int optional: Boolean! @@ -12912,6 +12993,7 @@ Answer associated with a particular question and answerable. input AnswerInput { questionId: ID! response: [String!] + answerableId: ID } """ diff --git a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/Flag.kt b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/Flag.kt new file mode 100644 index 0000000000..b2e6602537 --- /dev/null +++ b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/Flag.kt @@ -0,0 +1,52 @@ +package com.kickstarter.features.pledgedprojectsoverview.data + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +class Flag private constructor( + val icon: String?, + val message: String?, + val type: String?, +) : Parcelable { + + fun icon() = this.icon + fun message() = this.message + fun type() = this.type + @Parcelize + data class Builder( + var icon: String? = null, + var message: String? = null, + var type: String? = null + ) : Parcelable { + + fun icon(icon: String?) = apply { this.icon = icon } + fun message(message: String?) = apply { this.message = message } + fun type(type: String?) = apply { this.type = type } + fun build() = Flag( + icon = icon, + message = message, + type = type, + ) + } + + companion object { + fun builder() = Builder() + } + + fun toBuilder() = Builder( + icon = icon, + message = message, + type = type, + ) + + override fun equals(other: Any?): Boolean { + var equals = super.equals(other) + if (other is Flag) { + equals = icon() == other.icon() && + message() == other.message() && + type() == other.type() + } + return equals + } +} diff --git a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/PPOCard.kt b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/PPOCard.kt index cb16058604..5f0627952b 100644 --- a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/PPOCard.kt +++ b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/data/PPOCard.kt @@ -7,114 +7,120 @@ import type.CurrencyCode @Parcelize class PPOCard private constructor( - val backingId: String?, val address: String?, val addressID: String?, val amount: String?, + val backingId: String?, + val backingDetailsUrl: String?, val clientSecret: String?, + val creatorName: String?, val currencyCode: CurrencyCode?, val currencySymbol: String?, - val projectName: String?, + val flags: List?, + val imageContentDescription: String?, + val imageUrl: String?, val projectId: String?, + val projectName: String?, val projectSlug: String?, - val imageUrl: String?, - val imageContentDescription: String?, - val creatorName: String?, - val backingDetailsUrl: String?, val timeNumberForAction: Int, val viewType: PPOCardViewType? ) : Parcelable { - fun backingId() = this.backingId fun address() = this.address fun addressID() = this.addressID fun amount() = this.amount + fun backingDetailsUrl() = this.backingDetailsUrl + fun backingId() = this.backingId fun clientSecret() = this.clientSecret + fun creatorName() = this.creatorName fun currencyCode() = this.currencyCode fun currencySymbol() = this.currencySymbol - fun projectName() = this.projectName + fun flags() = this.flags + fun imageContentDescription() = this.imageContentDescription + fun imageUrl() = this.imageUrl fun projectId() = this.projectId + fun projectName() = this.projectName fun projectSlug() = this.projectSlug - fun imageUrl() = this.imageUrl - fun imageContentDescription() = this.imageContentDescription - fun creatorName() = this.creatorName - fun backingDetailsUrl() = this.backingDetailsUrl fun timeNumberForAction() = this.timeNumberForAction fun viewType() = this.viewType @Parcelize data class Builder( - var backingId: String? = null, var address: String? = null, var addressID: String? = null, var amount: String? = null, + var backingDetailsUrl: String? = null, + var backingId: String? = null, var clientSecret: String? = null, + var creatorName: String? = null, var currencyCode: CurrencyCode? = null, var currencySymbol: String? = null, - var projectName: String? = null, + var flags: List? = null, + var imageContentDescription: String? = null, + var imageUrl: String? = null, var projectId: String? = null, + var projectName: String? = null, var projectSlug: String? = null, - var imageUrl: String? = null, - var imageContentDescription: String? = null, - var creatorName: String? = null, - var backingDetailsUrl: String? = null, var timeNumberForAction: Int = 0, var viewType: PPOCardViewType? = null, ) : Parcelable { - fun backingId(backingId: String?) = apply { this.backingId = backingId } fun address(address: String?) = apply { this.address = address } fun addressID(addressID: String?) = apply { this.addressID = addressID } fun amount(amount: String?) = apply { this.amount = amount } + fun backingDetailsUrl(backingDetailsUrl: String?) = apply { this.backingDetailsUrl = backingDetailsUrl } + fun backingId(backingId: String?) = apply { this.backingId = backingId } fun clientSecret(clientSecret: String?) = apply { this.clientSecret = clientSecret } + fun creatorName(creatorName: String?) = apply { this.creatorName = creatorName } fun currencyCode(currencyCode: CurrencyCode?) = apply { this.currencyCode = currencyCode } fun currencySymbol(currencySymbol: String?) = apply { this.currencySymbol = currencySymbol } - fun projectName(projectName: String?) = apply { this.projectName = projectName } + fun flags(flags: List?) = apply { this.flags = flags } + fun imageContentDescription(imageContentDescription: String?) = apply { this.imageContentDescription = imageContentDescription } + fun imageUrl(imageUrl: String?) = apply { this.imageUrl = imageUrl } fun projectId(projectId: String?) = apply { this.projectId = projectId } + fun projectName(projectName: String?) = apply { this.projectName = projectName } fun projectSlug(projectSlug: String?) = apply { this.projectSlug = projectSlug } - fun imageUrl(imageUrl: String?) = apply { this.imageUrl = imageUrl } - fun imageContentDescription(imageContentDescription: String?) = apply { this.imageContentDescription = imageContentDescription } - fun creatorName(creatorName: String?) = apply { this.creatorName = creatorName } - fun backingDetailsUrl(backingDetailsUrl: String?) = apply { this.backingDetailsUrl = backingDetailsUrl } fun timeNumberForAction(timeNumberForAction: Int) = apply { this.timeNumberForAction = timeNumberForAction } fun viewType(viewType: PPOCardViewType?) = apply { this.viewType = viewType } fun build() = PPOCard( - backingId = backingId, address = address, addressID = addressID, amount = amount, + backingDetailsUrl = backingDetailsUrl, + backingId = backingId, clientSecret = clientSecret, + creatorName = creatorName, currencyCode = currencyCode, currencySymbol = currencySymbol, - projectName = projectName, + flags = flags, + imageContentDescription = imageUrl, + imageUrl = imageUrl, projectId = projectId, + projectName = projectName, projectSlug = projectSlug, - imageUrl = imageUrl, - imageContentDescription = imageUrl, - creatorName = creatorName, - backingDetailsUrl = backingDetailsUrl, timeNumberForAction = timeNumberForAction, viewType = viewType, ) } fun toBuilder() = Builder( - backingId = backingId, address = address, addressID = addressID, amount = amount, + backingDetailsUrl = backingDetailsUrl, + backingId = backingId, clientSecret = clientSecret, + creatorName = creatorName, currencyCode = currencyCode, currencySymbol = currencySymbol, - projectName = projectName, + flags = flags, + imageContentDescription = imageContentDescription, + imageUrl = imageUrl, projectId = projectId, + projectName = projectName, projectSlug = projectSlug, - imageUrl = imageUrl, - imageContentDescription = imageContentDescription, - creatorName = creatorName, - backingDetailsUrl = backingDetailsUrl, timeNumberForAction = timeNumberForAction, viewType = viewType, ) @@ -134,6 +140,7 @@ class PPOCard private constructor( clientSecret() == other.clientSecret() && currencyCode() == other.currencyCode() && currencySymbol() == other.currencySymbol() && + flags() == other.flags() && projectName() == other.projectName() && projectId() == other.projectId() && projectSlug() == other.projectSlug() && diff --git a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PPOCardView.kt b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PPOCardView.kt index 259e7e4707..fde596b73f 100644 --- a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PPOCardView.kt +++ b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PPOCardView.kt @@ -5,7 +5,10 @@ import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -32,9 +35,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest import com.kickstarter.R +import com.kickstarter.features.pledgedprojectsoverview.data.Flag +import com.kickstarter.ui.compose.designsystem.KSAlertBadge import com.kickstarter.ui.compose.designsystem.KSCoralBadge import com.kickstarter.ui.compose.designsystem.KSDividerLineGrey import com.kickstarter.ui.compose.designsystem.KSPrimaryBlackButton @@ -44,6 +50,7 @@ import com.kickstarter.ui.compose.designsystem.KSTheme import com.kickstarter.ui.compose.designsystem.KSTheme.colors import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions import com.kickstarter.ui.compose.designsystem.KSTheme.typography +import com.kickstarter.ui.compose.designsystem.KSWarningBadge import com.kickstarter.ui.compose.designsystem.shapes @Composable @@ -68,7 +75,7 @@ fun PPOCardPreview() { onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, onProjectPledgeSummaryClick = {}, - timeNumberForAction = 5 + flags = listOf(Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address").type("warning").icon("time").build()), ) Spacer(modifier = Modifier.height(dimensions.paddingMedium)) @@ -84,7 +91,7 @@ fun PPOCardPreview() { onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, onProjectPledgeSummaryClick = {}, - timeNumberForAction = 6 + flags = listOf(Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address").type("warning").icon("time").build()), ) Spacer(modifier = Modifier.height(dimensions.paddingMedium)) @@ -101,7 +108,7 @@ fun PPOCardPreview() { onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, onProjectPledgeSummaryClick = {}, - timeNumberForAction = 7 + flags = listOf(Flag.builder().build()), ) Spacer(modifier = Modifier.height(dimensions.paddingMedium)) @@ -118,7 +125,7 @@ fun PPOCardPreview() { onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, onProjectPledgeSummaryClick = {}, - timeNumberForAction = 8 + flags = listOf(Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address locks in 7 days").type("warning").icon("time").build(), Flag.builder().message("Address").type("warning").icon("time").build()), ) Spacer(modifier = Modifier.height(dimensions.paddingMedium)) @@ -137,7 +144,8 @@ enum class PPOCardViewType { enum class PPOCardViewTestTag { SHIPPING_ADDRESS_VIEW, - CONFIRM_ADDRESS_BUTTONS_VIEW + CONFIRM_ADDRESS_BUTTONS_VIEW, + FlAG_LIST_VIEW, } @Composable @@ -154,7 +162,7 @@ fun PPOCardView( shippingAddress: String? = null, onActionButtonClicked: () -> Unit, onSecondaryActionButtonClicked: () -> Unit, - timeNumberForAction: Int = 0, + flags: List? = null, ) { BadgedBox( @@ -171,12 +179,8 @@ fun PPOCardView( Column( Modifier.clickable { onCardClick.invoke() } ) { - when (viewType) { - PPOCardViewType.CONFIRM_ADDRESS -> ConfirmAddressAlertsView(timeNumberForAction) - PPOCardViewType.FIX_PAYMENT -> FixPaymentAlertsView(timeNumberForAction) - PPOCardViewType.AUTHENTICATE_CARD -> AuthenticateCardAlertsView(timeNumberForAction) - PPOCardViewType.OPEN_SURVEY -> TakeSurveyAlertsView(timeNumberForAction) - PPOCardViewType.UNKNOWN -> { } + if (!flags.isNullOrEmpty()) { + AlertFlagsView(flags = flags) } ProjectPledgeSummaryView( @@ -369,18 +373,34 @@ fun ShippingAddressView( } } +@OptIn(ExperimentalLayoutApi::class) @Composable -fun ConfirmAddressAlertsView(hoursRemaining: Int = -1) { - Column( +fun AlertFlagsView(flags: List) { + FlowRow( modifier = Modifier .fillMaxWidth() - .padding(top = dimensions.paddingMediumSmall, start = dimensions.paddingMediumSmall) + .testTag(PPOCardViewTestTag.FlAG_LIST_VIEW.name) + .padding(top = dimensions.paddingMediumSmall, start = dimensions.paddingMediumSmall, end = dimensions.paddingMediumSmall), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), ) { - if (hoursRemaining > 0) { - AddressLocksAlertView(hoursRemaining) + flags.forEach { + val icon = + when (it?.icon) { + "alert" -> ImageVector.vectorResource(id = R.drawable.ic_alert) + "time" -> ImageVector.vectorResource(id = R.drawable.ic_clock) + else -> null + } + + when (it?.type) { + "alert" -> KSAlertBadge(icon = icon, message = it.message) + "warning" -> KSWarningBadge(icon = icon, message = it.message) + else -> {} + } } } } + @Composable fun ConfirmAddressButtonsView(isConfirmButtonEnabled: Boolean, onEditAddressClicked: () -> Unit, onConfirmAddressClicked: () -> Unit) { Row( @@ -411,35 +431,6 @@ fun ConfirmAddressButtonsView(isConfirmButtonEnabled: Boolean, onEditAddressClic } } -@Composable -fun FixPaymentAlertsView(daysRemaining: Int = -1) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = dimensions.paddingMediumSmall, start = dimensions.paddingMediumSmall) - ) { - KSCoralBadge( - leadingIcon = { - // TODO: Replace with translated string - Image( - modifier = Modifier.padding(end = dimensions.paddingXSmall), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_icon_alert), - contentDescription = "Payment Failed", - colorFilter = ColorFilter.tint(colors.textAccentRedBold) - ) - }, - // TODO: Replace with translated string - text = "Payment failed", - textColor = colors.textAccentRedBold - ) - - if (daysRemaining > 0) { - Spacer(modifier = Modifier.height(dimensions.paddingSmall)) - PledgeWillBeDroppedAlert(daysRemaining) - } - } -} - @Composable fun FixPaymentButtonView(onFixPaymentClicked: () -> Unit) { // TODO: Replace with translated string @@ -452,34 +443,6 @@ fun FixPaymentButtonView(onFixPaymentClicked: () -> Unit) { ) } -@Composable -fun AuthenticateCardAlertsView(daysRemaining: Int = -1) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = dimensions.paddingMediumSmall, start = dimensions.paddingMediumSmall) - ) { - KSCoralBadge( - leadingIcon = { - // TODO: Replace with translated string - Image( - modifier = Modifier.padding(end = dimensions.paddingXSmall), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_icon_alert), - contentDescription = "Card needs authentication", - colorFilter = ColorFilter.tint(colors.textAccentRedBold) - ) - }, - // TODO: Replace with translated string - text = "Card needs authentication", - textColor = colors.textAccentRedBold - ) - - if (daysRemaining > 0) { - Spacer(modifier = Modifier.height(dimensions.paddingSmall)) - PledgeWillBeDroppedAlert(daysRemaining) - } - } -} @Composable fun AuthenticateCardButtonView(onAuthenticateCardClicked: () -> Unit) { // TODO: Replace with translated string @@ -492,34 +455,6 @@ fun AuthenticateCardButtonView(onAuthenticateCardClicked: () -> Unit) { ) } -@Composable -fun TakeSurveyAlertsView(hoursRemaining: Int = -1) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = dimensions.paddingMediumSmall, start = dimensions.paddingMediumSmall) - ) { - KSCoralBadge( - leadingIcon = { - // TODO: Replace with translated string - Image( - modifier = Modifier.padding(end = dimensions.paddingXSmall), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_icon_alert), - contentDescription = "Take Survey", - colorFilter = ColorFilter.tint(colors.textSecondary) - ) - }, - // TODO: Replace with translated string - text = "Take Survey", - ) - - if (hoursRemaining > 0) { - Spacer(modifier = Modifier.height(dimensions.paddingSmall)) - AddressLocksAlertView(hoursRemaining) - } - } -} - @Composable fun TakeSurveyButtonView(onAuthenticateCardClicked: () -> Unit) { // TODO: Replace with translated string diff --git a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PledgedProjectsOverviewScreen.kt b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PledgedProjectsOverviewScreen.kt index bba374ffcd..5e8ecbd91e 100644 --- a/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PledgedProjectsOverviewScreen.kt +++ b/app/src/main/java/com/kickstarter/features/pledgedprojectsoverview/ui/PledgedProjectsOverviewScreen.kt @@ -43,6 +43,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import com.kickstarter.R import com.kickstarter.features.pledgedprojectsoverview.data.PPOCard import com.kickstarter.features.pledgedprojectsoverview.data.PPOCardFactory +import com.kickstarter.libs.utils.extensions.format import com.kickstarter.libs.utils.extensions.isNullOrZero import com.kickstarter.ui.compose.designsystem.KSAlertDialog import com.kickstarter.ui.compose.designsystem.KSCircularProgressIndicator @@ -228,7 +229,7 @@ fun PledgedProjectsOverviewScreen( item { if (!totalAlerts.isNullOrZero()) { Text( - text = stringResource(id = R.string.alerts_fpo, totalAlerts), + text = stringResource(id = R.string.alerts_fpo).format("count", totalAlerts.toString()), style = typography.title3Bold, color = colors.textPrimary ) @@ -248,6 +249,7 @@ fun PledgedProjectsOverviewScreen( projectName = it.projectName(), pledgeAmount = it.amount(), imageUrl = it.imageUrl(), + flags = it.flags, imageContentDescription = it.imageContentDescription(), creatorName = it.creatorName(), sendAMessageClickAction = { onSendMessageClick(it.projectSlug() ?: "") }, @@ -268,7 +270,6 @@ fun PledgedProjectsOverviewScreen( } } }, - timeNumberForAction = it.timeNumberForAction() ) } } diff --git a/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt b/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt index 008f6f450d..8baadf8f73 100644 --- a/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt +++ b/app/src/main/java/com/kickstarter/libs/utils/extensions/StringExt.kt @@ -230,3 +230,40 @@ fun String?.toInteger(): Int? { } } else null } + +fun String.format(key1: String, value1: String?): String { + val substitutions: HashMap = object : HashMap() { + init { + put(key1, value1) + } + } + return this.replace(substitutions) +} +fun String.replace(substitutions: Map): String { + val builder = StringBuilder() + for (key in substitutions.keys) { + if (builder.isNotEmpty()) { + builder.append("|") + } + builder + .append("(%\\{") + .append(key) + .append("\\})") + } + + val pattern = Pattern.compile(builder.toString()) + val matcher = pattern.matcher(this) + val buffer = StringBuffer() + + while (matcher.find()) { + val key = NON_WORD_REGEXP.matcher(matcher.group()).replaceAll("") + val value = substitutions[key] + val replacement = Matcher.quoteReplacement(value ?: "") + matcher.appendReplacement(buffer, replacement) + } + matcher.appendTail(buffer) + + return buffer.toString() +} + +private val NON_WORD_REGEXP = Pattern.compile("[^\\w]") 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 93c1edcddd..ce0774823e 100644 --- a/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt +++ b/app/src/main/java/com/kickstarter/services/transformers/GraphQLTransformers.kt @@ -7,6 +7,7 @@ import TriggerThirdPartyEventMutation import UserPrivacyQuery import com.google.android.gms.common.util.Base64Utils import com.google.gson.Gson +import com.kickstarter.features.pledgedprojectsoverview.data.Flag import com.kickstarter.features.pledgedprojectsoverview.data.PPOCard import com.kickstarter.features.pledgedprojectsoverview.data.PledgeTierType import com.kickstarter.features.pledgedprojectsoverview.data.PledgedProjectsOverviewEnvelope @@ -920,6 +921,9 @@ fun pledgedProjectsOverviewEnvelopeTransformer(ppoResponse: PledgedProjectsOverv val ppoCards = ppoResponse.pledges()?.edges()?.map { val ppoBackingData = it.node()?.backing()?.fragments()?.ppoCard() + val flags = it.node()?.flags()?.map { flag -> + Flag.builder().message(flag.message()).icon(flag.icon()).type(flag.type()).build() + } PPOCard.builder() .backingId(ppoBackingData?.id()) .backingDetailsUrl(ppoBackingData?.backingDetailsPageUrl()) @@ -933,6 +937,7 @@ fun pledgedProjectsOverviewEnvelopeTransformer(ppoResponse: PledgedProjectsOverv .imageUrl(ppoBackingData?.project()?.fragments()?.full()?.image()?.url()) .creatorName(ppoBackingData?.project()?.creator()?.name()) .viewType(getTierType(it.node()?.tierType())) + .flags(flags) .addressID(ppoBackingData?.deliveryAddress()?.id()) .build() } diff --git a/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSBadges.kt b/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSBadges.kt index f4b115ca39..3b3f024466 100644 --- a/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSBadges.kt +++ b/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSBadges.kt @@ -1,6 +1,7 @@ package com.kickstarter.ui.compose.designsystem import android.content.res.Configuration +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -8,14 +9,20 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import com.kickstarter.R import com.kickstarter.ui.compose.designsystem.KSTheme.colors import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions import com.kickstarter.ui.compose.designsystem.KSTheme.typography +import fragment.Full.Image @Composable @Preview(name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO) @@ -34,6 +41,20 @@ fun KSBadgesPreview() { Spacer(modifier = Modifier.height(dimensions.listItemSpacingSmall)) KSCoralBadge(text = "3 days left") + + Spacer(modifier = Modifier.height(dimensions.listItemSpacingSmall)) + + KSAlertBadge( + icon = ImageVector.vectorResource(id = R.drawable.ic_alert), + message = "Payment Failed" + ) + + Spacer(modifier = Modifier.height(dimensions.listItemSpacingSmall)) + + KSWarningBadge( + icon = ImageVector.vectorResource(id = R.drawable.ic_clock), + message = "Address locks in 7 days" + ) } } } @@ -95,3 +116,80 @@ fun KSCoralBadge( ) } } + +@Composable +fun KSAlertBadge( + icon: ImageVector?, + message: String? +) { + if (!message.isNullOrEmpty()) { + Row( + modifier = Modifier + .background( + color = colors.backgroundDangerSubtle, + shape = shapes.small + ) + .padding( + start = dimensions.paddingMediumSmall, + top = dimensions.paddingSmall, + bottom = dimensions.paddingSmall, + end = dimensions.paddingMediumSmall + ) + ) { + if (icon != null) { + Image( + modifier = Modifier + .padding(end = dimensions.paddingXSmall) + .size(dimensions.alertIconSize), + imageVector = icon, + contentDescription = message, + colorFilter = ColorFilter.tint(colors.textAccentRedBold) + ) + } + + Text( + text = message, + color = colors.textAccentRedBold, + style = typography.footnoteMedium + ) + } + } +} + +@Composable +fun KSWarningBadge( + icon: ImageVector?, + message: String? +) { + if (!message.isNullOrEmpty()) { + Row( + modifier = Modifier + .background( + color = colors.backgroundAccentOrangeSubtle, + shape = shapes.small + ) + .padding( + start = dimensions.paddingMediumSmall, + top = dimensions.paddingSmall, + bottom = dimensions.paddingSmall, + end = dimensions.paddingMediumSmall + ) + ) { + if (icon != null) { + Image( + modifier = Modifier + .padding(end = dimensions.paddingXSmall) + .size(dimensions.alertIconSize), + imageVector = icon, + contentDescription = message, + colorFilter = ColorFilter.tint(colors.textSecondary) + ) + } + Text( + text = message, + color = colors.textSecondary, + style = typography.footnoteMedium + ) + } + } +} diff --git a/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSColors.kt b/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSColors.kt index 8480b4b7f5..3b01c6438d 100644 --- a/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSColors.kt +++ b/app/src/main/java/com/kickstarter/ui/compose/designsystem/KSColors.kt @@ -164,6 +164,7 @@ data class KSCustomColors( val backgroundAccentGreenSubtle: Color = Color.Unspecified, val backgroundAccentBlueBold: Color = Color.Unspecified, val backgroundAccentBlueSubtle: Color = Color.Unspecified, + val backgroundAccentOrangeSubtle: Color = Color.Unspecified, val backgroundAccentPurpleSubtle: Color = Color.Unspecified, val backgroundDangerBold: Color = Color.Unspecified, val backgroundDangerSubtle: Color = Color.Unspecified, @@ -276,6 +277,7 @@ val KSLightCustomColors = KSCustomColors( backgroundAccentGreenSubtle = green_02, backgroundAccentBlueBold = blue_06, backgroundAccentBlueSubtle = blue_02, + backgroundAccentOrangeSubtle = orange_02, backgroundAccentPurpleSubtle = purple_02, backgroundDangerBold = red_06, backgroundDangerSubtle = red_02, @@ -385,6 +387,7 @@ val KSDarkCustomColors = KSCustomColors( backgroundAccentGreenSubtle = green_09, backgroundAccentBlueBold = blue_05, backgroundAccentBlueSubtle = blue_09, + backgroundAccentOrangeSubtle = orange_09, backgroundAccentPurpleSubtle = purple_09, backgroundDangerBold = red_05, backgroundDangerSubtle = red_10, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e38ca963d5..00144e51db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,12 +93,12 @@ You\'re all caught up! When projects you\'ve backed need your attention, you\'ll see them here. Project Alerts - Alerts (%1$s) + Alerts (%{count}) Address confirmed! Need to change your address before it locks? Visit your backing details on our website. Backing details Something went wrong - Pull to refresh You\'ve been authenticated successfully! Pull to refresh. Authentication success. Please pull to refresh. - "Authentication failed. Please try again." + Authentication failed. Please try again. diff --git a/app/src/test/java/com/kickstarter/ui/activities/compose/PPOCardViewKtTest.kt b/app/src/test/java/com/kickstarter/ui/activities/compose/PPOCardViewKtTest.kt index d026320e85..247ccf5c70 100644 --- a/app/src/test/java/com/kickstarter/ui/activities/compose/PPOCardViewKtTest.kt +++ b/app/src/test/java/com/kickstarter/ui/activities/compose/PPOCardViewKtTest.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithTag import com.kickstarter.KSRobolectricTestCase +import com.kickstarter.features.pledgedprojectsoverview.data.Flag import com.kickstarter.features.pledgedprojectsoverview.ui.PPOCardView import com.kickstarter.features.pledgedprojectsoverview.ui.PPOCardViewTestTag import com.kickstarter.features.pledgedprojectsoverview.ui.PPOCardViewType @@ -16,9 +17,11 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { composeTestRule.onNodeWithTag(PPOCardViewTestTag.CONFIRM_ADDRESS_BUTTONS_VIEW.name, true) private val shippingAddressView = composeTestRule.onNodeWithTag(PPOCardViewTestTag.SHIPPING_ADDRESS_VIEW.name, true) + private val flagsListView = + composeTestRule.onNodeWithTag(PPOCardViewTestTag.FlAG_LIST_VIEW.name, true) + @Test fun testConfirmAddressView() { - val timeNumberForAction = 5 composeTestRule.setContent { KSTheme { PPOCardView( @@ -32,13 +35,9 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { shippingAddress = "Firsty Lasty\n123 First Street, Apt #5678\nLos Angeles, CA 90025-1234\nUnited States", onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, - timeNumberForAction = timeNumberForAction ) } } - // Alerts - // TODO: Replace with translated string - composeTestRule.onAllNodesWithText("Address locks in $timeNumberForAction hours")[0].assertIsDisplayed() // Shipping address displayed shippingAddressView.assertIsDisplayed() // CTA @@ -47,7 +46,6 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { @Test fun testFixPaymentView() { - val timeNumberForAction = 6 composeTestRule.setContent { KSTheme { PPOCardView( @@ -60,15 +58,10 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { sendAMessageClickAction = {}, onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, - timeNumberForAction = timeNumberForAction ) } } - // Alerts - // TODO: Replace with translated strings - composeTestRule.onAllNodesWithText("Payment failed")[0].assertIsDisplayed() - composeTestRule.onAllNodesWithText("Pledge will be dropped in $timeNumberForAction days")[0].assertIsDisplayed() // Shipping address hidden shippingAddressView.assertIsNotDisplayed() // CTA @@ -77,7 +70,6 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { @Test fun testAuthenticateCardView() { - val timeNumberForAction = 7 composeTestRule.setContent { KSTheme { PPOCardView( @@ -90,15 +82,10 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { sendAMessageClickAction = {}, onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, - timeNumberForAction = timeNumberForAction ) } } - // Alerts - // TODO: Replace with translated strings - composeTestRule.onAllNodesWithText("Card needs authentication")[0].assertIsDisplayed() - composeTestRule.onAllNodesWithText("Pledge will be dropped in $timeNumberForAction days")[0].assertIsDisplayed() // Shipping address hidden shippingAddressView.assertIsNotDisplayed() // CTA @@ -107,7 +94,6 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { @Test fun testTakeSurveyView() { - val timeNumberForAction = 8 composeTestRule.setContent { KSTheme { PPOCardView( @@ -120,17 +106,65 @@ class PPOCardViewKtTest : KSRobolectricTestCase() { sendAMessageClickAction = {}, onActionButtonClicked = {}, onSecondaryActionButtonClicked = {}, - timeNumberForAction = timeNumberForAction ) } } - // Alerts - // TODO: Replace with translated strings - composeTestRule.onAllNodesWithText("Address locks in $timeNumberForAction hours")[0].assertIsDisplayed() // Shipping address hidden shippingAddressView.assertIsNotDisplayed() // CTA composeTestRule.onAllNodesWithText("Take Survey")[0].assertIsDisplayed() } + + @Test + fun testVisibleFlags() { + composeTestRule.setContent { + KSTheme { + PPOCardView( + viewType = PPOCardViewType.OPEN_SURVEY, + onCardClick = {}, + onProjectPledgeSummaryClick = {}, + projectName = "Sugardew Island - Your cozy farm shop let’s pretend this is a longer title let’s pretend this is a longer title", + pledgeAmount = "$70.00", + creatorName = "Some really really really really really really really long name", + sendAMessageClickAction = {}, + onActionButtonClicked = {}, + onSecondaryActionButtonClicked = {}, + flags = listOf( + Flag.builder().message("Address locks in 7 days").type("alert").icon("time") + .build(), + Flag.builder().message("Open Survey").type("warning").icon("time").build() + ), + ) + } + } + + flagsListView.assertIsDisplayed() + composeTestRule.onAllNodesWithText("Address locks in 7 days")[0].assertIsDisplayed() + composeTestRule.onAllNodesWithText("Open Survey")[0].assertIsDisplayed() + } + + @Test + fun testInvisibleFlags() { + composeTestRule.setContent { + KSTheme { + PPOCardView( + viewType = PPOCardViewType.OPEN_SURVEY, + onCardClick = {}, + onProjectPledgeSummaryClick = {}, + projectName = "Sugardew Island - Your cozy farm shop let’s pretend this is a longer title let’s pretend this is a longer title", + pledgeAmount = "$70.00", + creatorName = "Some really really really really really really really long name", + sendAMessageClickAction = {}, + onActionButtonClicked = {}, + onSecondaryActionButtonClicked = {}, + flags = listOf(Flag.builder().message("Address locks in 7 days").type(null).icon("time").build(), Flag.builder().message("Open Survey").type("warning").icon("time").build()), + ) + } + } + + flagsListView.assertIsDisplayed() + composeTestRule.onAllNodesWithText("Address locks in 7 days")[0].assertIsNotDisplayed() + composeTestRule.onAllNodesWithText("Open Survey")[0].assertIsDisplayed() + } }