Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MBL-1495] [MBL-1502] PPO UI implementation #2186

Merged
merged 19 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ struct PPOAddressSummary: View {
HStack(alignment: .firstTextBaseline) {
// TODO: Localize
Text("Shipping address")
.font(Font(PPOCardStyles.title.font))
.foregroundStyle(Color(PPOCardStyles.title.color))
.font(Font(PPOStyles.title.font))
.foregroundStyle(Color(PPOStyles.title.color))
.frame(width: self.leadingColumnWidth, alignment: Constants.textAlignment)

Text(self.address)
.font(Font(PPOCardStyles.body.font))
.foregroundStyle(Color(PPOCardStyles.body.color))
.font(Font(PPOStyles.body.font))
.foregroundStyle(Color(PPOStyles.body.color))
.frame(maxWidth: Constants.maxWidth, alignment: Constants.textAlignment)
}
.frame(maxWidth: Constants.maxWidth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct PPOAlertFlag: View {
Spacer()
.frame(width: Constants.spacerWidth)
Text(self.alert.message)
.font(Font(PPOCardStyles.title.font))
.font(Font(PPOStyles.title.font))
.foregroundStyle(self.foregroundColor)
}
.padding(Constants.padding)
Expand All @@ -27,29 +27,29 @@ struct PPOAlertFlag: View {
}

var image: Image {
switch self.alert.type {
switch self.alert.icon {
case .time:
Image(PPOCardStyles.timeImage)
Image(PPOStyles.timeImage)
case .alert:
Image(PPOCardStyles.alertImage)
Image(PPOStyles.alertImage)
}
}

var foregroundColor: Color {
switch self.alert.icon {
switch self.alert.type {
case .warning:
Color(uiColor: PPOCardStyles.warningColor.foreground)
Color(uiColor: PPOStyles.warningColor.foreground)
case .alert:
Color(uiColor: PPOCardStyles.alertColor.foreground)
Color(uiColor: PPOStyles.alertColor.foreground)
}
}

var backgroundColor: Color {
switch self.alert.icon {
switch self.alert.type {
case .warning:
Color(uiColor: PPOCardStyles.warningColor.background)
Color(uiColor: PPOStyles.warningColor.background)
case .alert:
Color(uiColor: PPOCardStyles.alertColor.background)
Color(uiColor: PPOStyles.alertColor.background)
}
}

Expand All @@ -64,10 +64,10 @@ struct PPOAlertFlag: View {

#Preview("Stack of flags") {
VStack(alignment: .leading, spacing: 8) {
PPOAlertFlag(alert: .init(type: .time, icon: .warning, message: "Address locks in 8 hours"))
PPOAlertFlag(alert: .init(type: .alert, icon: .warning, message: "Survey available"))
PPOAlertFlag(alert: .init(type: .warning, icon: .time, message: "Address locks in 8 hours"))
PPOAlertFlag(alert: .init(type: .warning, icon: .alert, message: "Survey available"))
PPOAlertFlag(alert: .init(type: .alert, icon: .alert, message: "Payment failed"))
PPOAlertFlag(alert: .init(type: .time, icon: .alert, message: "Pledge will be dropped in 6 days"))
PPOAlertFlag(alert: .init(type: .alert, icon: .time, message: "Pledge will be dropped in 6 days"))
PPOAlertFlag(alert: .init(type: .alert, icon: .alert, message: "Card needs authentication"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import SwiftUI

struct PPOProjectCard: View {
@StateObject var viewModel: PPOProjectCardViewModel
var parentSize: CGSize

var onViewBackingDetails: ((PPOProjectCardModel) -> Void)? = nil
var onSendMessage: ((PPOProjectCardModel) -> Void)? = nil
var onPerformAction: ((PPOProjectCardModel, PPOProjectCardModel.Action) -> Void)? = nil
stevestreza-ksr marked this conversation as resolved.
Show resolved Hide resolved

var body: some View {
VStack(spacing: Constants.spacing) {
self.flagList
self.projectDetails(leadingColumnWidth: self.viewModel.parentSize.width * Constants.firstColumnWidth)
self.projectDetails(leadingColumnWidth: self.parentSize.width * Constants.firstColumnWidth)
self.divider
self.projectCreator
self.divider
self.addressDetails(leadingColumnWidth: self.viewModel.parentSize.width * Constants.firstColumnWidth)
self.addressDetails(leadingColumnWidth: self.parentSize.width * Constants.firstColumnWidth)
self.actionButtons
}
.padding(.vertical)
Expand All @@ -32,8 +37,16 @@ struct PPOProjectCard: View {
content: { self.badge.opacity(self.viewModel.card.isUnread ? 1 : 0) }
)

// insets
.padding(.horizontal, Constants.outerPadding)
// Handle actions
.onReceive(self.viewModel.viewBackingDetailsTapped) {
self.onViewBackingDetails?(self.viewModel.card)
}
.onReceive(self.viewModel.sendMessageTapped) {
self.onSendMessage?(self.viewModel.card)
}
.onReceive(self.viewModel.actionPerformed) { action in
self.onPerformAction?(self.viewModel.card, action)
}
}

@ViewBuilder
Expand All @@ -44,7 +57,7 @@ struct PPOProjectCard: View {
@ViewBuilder
private var badge: some View {
Circle()
.fill(Color(uiColor: PPOCardStyles.badgeColor))
.fill(Color(uiColor: PPOStyles.badgeColor))
.frame(width: Constants.badgeSize, height: Constants.badgeSize)
.offset(x: Constants.badgeSize / 2, y: -(Constants.badgeSize / 2))
}
Expand All @@ -67,7 +80,7 @@ struct PPOProjectCard: View {
@ViewBuilder
private func projectDetails(leadingColumnWidth: CGFloat) -> some View {
PPOProjectDetails(
imageUrl: self.viewModel.card.imageURL,
image: self.viewModel.card.image,
title: self.viewModel.card.title,
pledge: self.viewModel.card.pledge,
leadingColumnWidth: leadingColumnWidth
Expand All @@ -77,8 +90,13 @@ struct PPOProjectCard: View {

@ViewBuilder
private var projectCreator: some View {
PPOProjectCreator(creatorName: self.viewModel.card.creatorName)
.padding([.horizontal])
PPOProjectCreator(
creatorName: self.viewModel.card.creatorName,
onSendMessage: { [weak viewModel] () in
viewModel?.sendCreatorMessage()
}
)
.padding([.horizontal])
}

@ViewBuilder
Expand Down Expand Up @@ -146,7 +164,10 @@ struct PPOProjectCard: View {
ScrollView(.vertical) {
VStack(spacing: 16) {
ForEach(PPOProjectCardModel.previewTemplates) { template in
PPOProjectCard(viewModel: PPOProjectCardViewModel(card: template, parentSize: geometry.size))
PPOProjectCard(
viewModel: PPOProjectCardViewModel(card: template),
parentSize: geometry.size
)
}
}
}
Expand Down
stevestreza-ksr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Foundation
import Kingfisher
import KsApi
import Library

public struct PPOProjectCardModel: Identifiable, Equatable {
public struct PPOProjectCardModel: Identifiable, Equatable, Hashable {
public let isUnread: Bool
public let alerts: [Alert]
public let imageURL: URL
public let image: Kingfisher.Source
public let title: String
public let pledge: GraphAPI.MoneyFragment
public let creatorName: String
Expand All @@ -15,6 +16,19 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
public let backingDetailsUrl: String
public let projectAnalytics: GraphAPI.ProjectAnalyticsFragment

public func hash(into hasher: inout Hasher) {
hasher.combine(self.isUnread)
hasher.combine(self.alerts)
hasher.combine(self.image)
hasher.combine(self.title)
hasher.combine(self.pledge)
hasher.combine(self.creatorName)
hasher.combine(self.address)
stevestreza-ksr marked this conversation as resolved.
Show resolved Hide resolved
hasher.combine(self.actions.0)
hasher.combine(self.actions.1)
hasher.combine(self.tierType)
}

// MARK: - Identifiable

public let id = UUID()
Expand All @@ -26,7 +40,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
public static func == (lhs: PPOProjectCardModel, rhs: PPOProjectCardModel) -> Bool {
lhs.isUnread == rhs.isUnread &&
lhs.alerts == rhs.alerts &&
lhs.imageURL == rhs.imageURL &&
lhs.image == rhs.image &&
lhs.title == rhs.title &&
lhs.pledge == rhs.pledge &&
lhs.creatorName == rhs.creatorName &&
Expand All @@ -41,7 +55,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
case confirmAddress
}

public enum Action: Identifiable, Equatable {
public enum Action: Identifiable, Equatable, Hashable {
case confirmAddress
case editAddress
case completeSurvey
Expand Down Expand Up @@ -98,7 +112,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
}
}

public struct Alert: Identifiable, Equatable {
public struct Alert: Identifiable, Equatable, Hashable {
public let type: AlertType
public let icon: AlertIcon
public let message: String
Expand All @@ -113,7 +127,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
"\(self.type)-\(self.icon)-\(self.message)"
}

public enum AlertType: Identifiable, Equatable {
public enum AlertIcon: Identifiable, Equatable {
case time
case alert

Expand All @@ -127,7 +141,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {
}
}

public enum AlertIcon: Identifiable, Equatable {
public enum AlertType: Identifiable, Equatable {
case warning
case alert

Expand All @@ -145,7 +159,7 @@ public struct PPOProjectCardModel: Identifiable, Equatable {

extension PPOProjectCardModel.Alert {
init?(flag: GraphAPI.PpoCardFragment.Flag) {
let alertType: PPOProjectCardModel.Alert.AlertType? = switch flag.type {
let alertIcon: PPOProjectCardModel.Alert.AlertIcon? = switch flag.icon {
case "alert":
.alert
case "time":
Expand All @@ -154,7 +168,7 @@ extension PPOProjectCardModel.Alert {
nil
}

let alertIcon: PPOProjectCardModel.Alert.AlertIcon? = switch flag.icon {
let alertType: PPOProjectCardModel.Alert.AlertType? = switch flag.type {
case "alert":
.alert
case "warning":
Expand All @@ -180,6 +194,14 @@ extension GraphAPI.MoneyFragment: Equatable {
}
}

extension GraphAPI.MoneyFragment: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(self.amount)
hasher.combine(self.currency)
hasher.combine(self.symbol)
}
}

extension PPOProjectCardModel {
#if targetEnvironment(simulator)
public static let previewTemplates: [PPOProjectCardModel] = [
Expand All @@ -194,9 +216,9 @@ extension PPOProjectCardModel {
internal static let confirmAddressTemplate = PPOProjectCardModel(
isUnread: true,
alerts: [
.init(type: .time, icon: .warning, message: "Address locks in 8 hours")
.init(type: .warning, icon: .time, message: "Address locks in 8 hours")
],
imageURL: URL(string: "http://localhost/")!,
image: .network(URL(string: "https:///")!),
title: "Sugardew Island - Your cozy farm shop let’s pretend this is a way way way longer title",
pledge: .init(amount: "50.00", currency: .usd, symbol: "$"),
creatorName: "rokaplay truncate if longer than",
Expand All @@ -215,10 +237,10 @@ extension PPOProjectCardModel {
internal static let addressLockTemplate = PPOProjectCardModel(
isUnread: true,
alerts: [
.init(type: .alert, icon: .warning, message: "Survey available"),
.init(type: .time, icon: .warning, message: "Address locks in 48 hours")
.init(type: .warning, icon: .alert, message: "Survey available"),
.init(type: .warning, icon: .time, message: "Address locks in 48 hours")
],
imageURL: URL(string: "http://localhost/")!,
image: .network(URL(string: "https:///")!),
title: "Sugardew Island - Your cozy farm shop let’s pretend this is a way way way longer title",
pledge: .init(amount: "50.00", currency: .usd, symbol: "$"),
creatorName: "rokaplay truncate if longer than",
Expand All @@ -234,12 +256,12 @@ extension PPOProjectCardModel {
alerts: [
.init(type: .alert, icon: .alert, message: "Payment failed"),
.init(
type: .time,
icon: .alert,
type: .alert,
icon: .time,
message: "Pledge will be dropped in 6 days"
)
],
imageURL: URL(string: "http://localhost/")!,
image: .network(URL(string: "https:///")!),
title: "Sugardew Island - Your cozy farm shop let’s pretend this is a way way way longer title",
pledge: .init(amount: "50.00", currency: .usd, symbol: "$"),
creatorName: "rokaplay truncate if longer than",
Expand All @@ -255,12 +277,12 @@ extension PPOProjectCardModel {
alerts: [
.init(type: .alert, icon: .alert, message: "Card needs authentication"),
.init(
type: .time,
icon: .alert,
type: .alert,
icon: .time,
message: "Pledge will be dropped in 6 days"
)
],
imageURL: URL(string: "http://localhost/")!,
image: .network(URL(string: "https:///")!),
title: "Sugardew Island - Your cozy farm shop let’s pretend this is a way way way longer title",
pledge: .init(amount: "50.00", currency: .usd, symbol: "$"),
creatorName: "rokaplay truncate if longer than",
Expand All @@ -274,9 +296,9 @@ extension PPOProjectCardModel {
internal static let completeSurveyTemplate = PPOProjectCardModel(
isUnread: true,
alerts: [
.init(type: .alert, icon: .warning, message: "Survey available")
.init(type: .warning, icon: .alert, message: "Survey available")
],
imageURL: URL(string: "http://localhost/")!,
image: .network(URL(string: "https:///")!),
title: "Sugardew Island - Your cozy farm shop let’s pretend this is a way way way longer title",
pledge: .init(amount: "50.00", currency: .usd, symbol: "$"),
creatorName: "rokaplay truncate if longer than",
Expand Down Expand Up @@ -330,8 +352,9 @@ extension PPOProjectCardModel {
let backing = card.backing?.fragments.ppoBackingFragment
let ppoProject = backing?.project?.fragments.ppoProjectFragment

let imageURL = ppoProject?.image?.url
let image = ppoProject?.image?.url
.flatMap { URL(string: $0) }
.map { Kingfisher.Source.network($0) }

let title = ppoProject?.name
let pledge = backing?.amount.fragments.moneyFragment
Expand Down Expand Up @@ -373,12 +396,11 @@ extension PPOProjectCardModel {

let projectAnalyticsFragment = backing?.project?.fragments.projectAnalyticsFragment

if let imageURL, let title, let pledge, let creatorName, let projectAnalyticsFragment,
let backingDetailsUrl {
if let image, let title, let pledge, let creatorName, let projectAnalyticsFragment, let backingDetailsUrl {
self.init(
isUnread: true,
alerts: alerts,
imageURL: imageURL,
image: image,
title: title,
pledge: pledge,
creatorName: creatorName,
Expand Down
Loading