From 83ae7375af6cb3a48075dc30e7abcd0d081881dd Mon Sep 17 00:00:00 2001 From: Peter-John Welcome Date: Mon, 29 Jun 2026 13:29:14 +0200 Subject: [PATCH] Add provider logos and redesign Mobile Money channel selection list Replace the generic placeholder icons with provider-specific logos (M-Pesa, MTN, Airtel/ATL, Telecel/Vodafone) and a dedicated bank transfer logo, wiring each mobile money key to its own asset in SupportedChannel. Rework ChannelSelectionView from a two-column LazyVGrid into a full- width vertical list of tappable rows. Each channel is now a PlainButton with a leading logo and single-line, scalable title, giving a larger tap target and consistent layout regardless of channel count. If you'd prefer something shorter for a squash merge: Add provider logos and redesign Mobile Money channel selection list --- .../Views/ChannelSelectionView.swift | 41 +++++++++--------- .../Charge/Models/SupportedChannel.swift | 26 ++++++----- .../atlLogo.imageset/Airtel - Icon.svg | 3 ++ .../atlLogo.imageset/Contents.json | 21 +++++++++ .../bankTransferLogo.imageset/Contents.json | 23 ++++++++++ .../transfer 1-2.png | Bin 0 -> 1238 bytes .../transfer 1-3.png | Bin 0 -> 1601 bytes .../bankTransferLogo.imageset/transfer 1.png | Bin 0 -> 721 bytes .../mpesaLogo.imageset/Contents.json | 21 +++++++++ .../mpesaLogo.imageset/M-PESA Icon.svg | 6 +++ .../mtnLogo.imageset/Contents.json | 21 +++++++++ .../mtnLogo.imageset/MTN Logo.svg | 12 +++++ .../vodLogo.imageset/Contents.json | 21 +++++++++ .../vodLogo.imageset/Telecel-Icon.svg | 11 +++++ 14 files changed, 175 insertions(+), 31 deletions(-) create mode 100644 Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Airtel - Icon.svg create mode 100644 Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Contents.json create mode 100644 Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/Contents.json create mode 100644 Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-2.png create mode 100644 Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-3.png create mode 100644 Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1.png create mode 100644 Sources/PaystackUI/Images/Images.xcassets/mpesaLogo.imageset/Contents.json create mode 100644 Sources/PaystackUI/Images/Images.xcassets/mpesaLogo.imageset/M-PESA Icon.svg create mode 100644 Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/Contents.json create mode 100644 Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/MTN Logo.svg create mode 100644 Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Contents.json create mode 100644 Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Telecel-Icon.svg diff --git a/Sources/PaystackUI/Charge/MobileMoney/Views/ChannelSelectionView.swift b/Sources/PaystackUI/Charge/MobileMoney/Views/ChannelSelectionView.swift index b82271c..679fa93 100644 --- a/Sources/PaystackUI/Charge/MobileMoney/Views/ChannelSelectionView.swift +++ b/Sources/PaystackUI/Charge/MobileMoney/Views/ChannelSelectionView.swift @@ -1,5 +1,6 @@ import SwiftUI import PaystackCore + @available(iOS 14.0, *) struct ChannelSelectionView: View { @@ -8,7 +9,6 @@ struct ChannelSelectionView: View { @StateObject var viewModel: ChannelSelectionViewModel let supportedChannels: [SupportedChannel] - let columns = [GridItem(.flexible()), GridItem(.flexible())] init(state: Binding, supportedChannels: [SupportedChannel], @@ -25,19 +25,18 @@ struct ChannelSelectionView: View { .font(.body16M) .foregroundColor(.stackBlue) .multilineTextAlignment(.center) - GeometryReader { geo in - LazyVGrid(columns: columns) { - ForEach(supportedChannels) { channel in - ChannelView(channelTitle: channel.displayTitle, image: channel.image) - .padding(.singlePadding) - .onTapGesture { - viewModel.choose(channel) - } - .frame(width: (geo.size.width / CGFloat(supportedChannels.count)).rounded()) + + VStack(spacing: .singlePadding) { + ForEach(supportedChannels) { channel in + Button(action: { viewModel.choose(channel) }) { + ChannelView(channelTitle: channel.displayTitle, + image: channel.image) } + .buttonStyle(PlainButtonStyle()) } } } + .padding(.horizontal, .doublePadding) } } } @@ -77,21 +76,23 @@ struct ChannelView: View { var body: some View { - HStack(spacing: .singlePadding) { + HStack(spacing: .doublePadding) { + image + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: imageSlotSize, height: imageSlotSize) - VStack(alignment: .leading, spacing: .singlePadding) { - image - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: imageSlotSize, height: imageSlotSize) + Text(channelTitle) + .font(.body16M) + .foregroundColor(.stackBlue) + .lineLimit(1) + .minimumScaleFactor(0.85) - Text(channelTitle) - .font(.body14M) - .foregroundColor(.navy02) - } Spacer() } .padding(.doublePadding) + .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(Rectangle()) .cornerRadius(.cornerRadius) .overlay( RoundedRectangle(cornerRadius: .cornerRadius) diff --git a/Sources/PaystackUI/Charge/Models/SupportedChannel.swift b/Sources/PaystackUI/Charge/Models/SupportedChannel.swift index c35e443..95c5c43 100644 --- a/Sources/PaystackUI/Charge/Models/SupportedChannel.swift +++ b/Sources/PaystackUI/Charge/Models/SupportedChannel.swift @@ -6,7 +6,7 @@ enum SupportedChannel: Equatable, Identifiable { case mobileMoney(MobileMoneyChannel) case bankTransfer(BankTransferConfig) case zap(ZapConfig) - + var id: String { switch self { case .card: @@ -19,7 +19,7 @@ enum SupportedChannel: Equatable, Identifiable { return "zap" } } - + var displayTitle: String { switch self { case .card: @@ -27,12 +27,12 @@ enum SupportedChannel: Equatable, Identifiable { case .mobileMoney(let channel): return channel.value case .bankTransfer: - return "Transfer" + return "Bank Transfer" case .zap: return "Zap" } } - + var image: Image { switch self { case .card: @@ -40,20 +40,24 @@ enum SupportedChannel: Equatable, Identifiable { case .mobileMoney(let channel): return Self.image(forMobileMoneyKey: channel.key) case .bankTransfer: - return Image(systemName: "building.columns") + return Image("bankTransferLogo", bundle: .current) case .zap: return Image("zapSingleLogo", bundle: .current) } } - + private static func image(forMobileMoneyKey key: String) -> Image { switch key.uppercased() { - case "MPESA", "ATL_KE": - return Image("kenyaSHLogo", bundle: .current) - case "MTN", "ATL", "VOD": - return Image("kenyaSHLogo", bundle: .current) + case "MPESA": + return Image("mpesaLogo", bundle: .current) + case "ATL_KE", "ATL": + return Image("atlLogo", bundle: .current) + case "MTN": + return Image("mtnLogo", bundle: .current) + case "VOD": + return Image("vodLogo", bundle: .current) default: - return Image(systemName: "creditcard") + return Image(systemName: "kenyaSHLogo") } } } diff --git a/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Airtel - Icon.svg b/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Airtel - Icon.svg new file mode 100644 index 0000000..60ac670 --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Airtel - Icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Contents.json b/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Contents.json new file mode 100644 index 0000000..64c7784 --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/atlLogo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Airtel - Icon.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/Contents.json b/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/Contents.json new file mode 100644 index 0000000..260d0b2 --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "transfer 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "transfer 1-2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "transfer 1-3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-2.png b/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-2.png new file mode 100644 index 0000000000000000000000000000000000000000..da973af7512a1333c9725d9303bde4f96c5fe6d8 GIT binary patch literal 1238 zcmV;{1S$K8P)7%Q6n-;yT_@qkDyphLOC5oVlF-CflJavv zHe67-B+7vb3B^T7ATAC!gg~(+RP_RJFBKpp8zfK;RD?r;3#l*kKvJ#KDhVKH7E%aM zXctiG)b-BrW??tG_O5@{UbTmO(rV}Dz1h$2y?JlO0NZE{1lkBmS_v~BPg0efh(>#5 zXam|pP?AqDnmc{PDb8WCNmYeVk{LSKn{Gy_DS``&-y{K8 z3Y#H&30?qBWI`Zf=kotQ*i?KrL~#DXN{nzmRm+_mczR^8Kc)8<7MFAQnsAbD)a^A1 z{^!nX)0~q;okirK!M+}=t`t(~&Z|%d==KyX2Qe-6D0cJIN%7o;l_`}LSL=}zKzCLx z9*suDm{M1l@f4#)>D>9Hgx!3V1%*2)<5{N|a)9Dv9X)0%XuF&e37SK}$Hu;iyg7{%Wv?#dt(}Py@lUo{)_o?KlSr%nhEvqsZ^8vBc++}$!bar0KUQzyv zKn?8KeOGQ`^suSI1QmV5+2+Cd#ibMhGG!(>JkS?6%V4Zo!i-ZAA34~Y*ZW6aOoa(8 zodMHE>FNx^V-F9(=W}0Gf0RI>xFZms&7>@;$qyc2bTSnpB`Z0B*OJe}{=MDM*%5?O zr@yo+afo!>xbX?LEQWAQ)U6;`DkZ@t#b`SmF@E@Q7=P-CYDLat&%E?%0=y)cKR6eSuo@@Uza3W@yO&WVZGb-7)(C*bn+6Wm3p{*MS!1evWRBT~)lm~wP-Z)Dzsjcc36JXR7} zZ?}Du1OkCsXnGLv#|oYi%!x|C`TO9GoHzwpE>)f+YK$RKDyvS@FFZS5 zlN>^xIe9DxUK5;o`?!oWS5B4m@_5yOtPaJ{E(U zkC3<(>K%&(@v4_ER~#5`2K+4O`tQ1JY@^-y7qc_ZX5~yks{jB107*qoM6N<$f|5Hh AApigX literal 0 HcmV?d00001 diff --git a/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-3.png b/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1-3.png new file mode 100644 index 0000000000000000000000000000000000000000..b45c2299157ec05dddc6600d99e64feb0c079a30 GIT binary patch literal 1601 zcmV-H2EO@;P)L3VmOVXGSgm|D1 zNC?3Ljv}E33B{5600b%Fu>}-IRD~zvN~l5zDXu^$F9`Ds;_y`E!p2FRRH4YOh$tmW zV<=BVTscv+K*ck-rDaJ2N{w-_DtHzB#i1Ra8+$F)%r|;E|-51Ii2vWdWHu(#HEDV_>VqhRL|7Nq|@zo#i7I~3*qUxg+V5>0sO0L z!i2COP12!K5-%xXZoX7zBMiVPdJhgL4ulQ0wHyyZ2~ZNklCf$Ic_69;fo^d%KzeVZn{6K2*18G`}Qd-#B8uRe!=Z-ObFit2EfA%n&Y4;?@J*3 zY|F6@t?H@Bl>iFAelXmvF*6sEmfU<%0*-JwXg1W~dX4MW6Jjd-@b!=CZDcAkFY)D( zuYF#^TqPyxT1-O{8Wee&X3WcFHNm-0fj2>}KlQyH0@_|v((cbI|XP5H<%9M>2 z>sfkcPShS=s7lfsNUoQTp~G4m!@q^ju;uk%Z8MBb32W zMu@l6&qK@kp#8+L0RL}Gudr>~;I zpt!IQoMd$I8eEGn!Ee9cRBcmNcMm+_d<>2^Jr0jP@{ndaWDJ^(*8&G)| ze{(!2I)ZVKiC^u0f*}Ufae>Js1UeYMJmHNl#(LH_Hido3f&C5e;`7h+HaVT~?7DV$ zW*E%tHh98KEkvO*3bwVldR6a<%rmue6#cz=60p1PL)>*S;mt@+{{a5)E}qxjLY6&i zsfF&ovyv9s!}lMAPtUv$M~^(L+Xud7|6A{!)m@GiL3n3o)rAKq`%(XCh3v-zr-ev& zzk5d0N-QUgGvd=TW7m_7NP1^E@2j^9k;b(%cxu4!RI9R_FwTgs3I25XXZZ4)?{}{S zNju2a)`(h#rG%B)71%JCBF&h`JK)dp^p+Av9o-|^)(q)=o0~aH#WKP^{`PSejND_C zG``T2*=07c2&dk7#Wyh%1*2GASyhOm&`_>Zq(2FK4cC`$WHwewDp)2waC@lQ#w_lv zt*eq|wQ}QV;Inw@x?+S!q2M$9&f8si+wB>`%ePkH^RK?mDPISBMhe%mHT-7ZklO~E z5|}{OYcE6B>o4tIbF$~qoR?Q}($k4hz@JfQ*$g+-LL9Rm!U^;g43OncBPaE18Q~zl zc}(#b%0(L@3yNdBlwD@E@DM%?G)h)MtsKrSvXt=X7w5ugcHw;OOp(Q8LwYz$!TQZZ zo>_ji;{`Kh;~0MfY893f=Ch0nM3XdERq_ADgdkb2raIMTm!l%$@aVh7o=1mrhoAwo2h_#F z-?#sT>DjBWbmI?9!mno3agv7*9fBj6k8W#rDLLvKXdJ+66V})zSq1(AQs8-L0VHbV z`o`?fAyl=r^36pBk>wC1nGVOcZJqCj!|8>pPHP+NML(;x1hVYOE>a~-XwnON07N{+ zLmkRz19v$vr{dsW7#R7isfsG9sG^E0%835}8yBLNHHx)v00000NkvXXu0mjfHC*=9 literal 0 HcmV?d00001 diff --git a/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1.png b/Sources/PaystackUI/Images/Images.xcassets/bankTransferLogo.imageset/transfer 1.png new file mode 100644 index 0000000000000000000000000000000000000000..e8538dda308fe22d426343430d8664dbc617e718 GIT binary patch literal 721 zcmV;?0xtcDP)@r0n~xbWnHW4DJsUVP^&E7z|bk!$QZoDz|v+h zBm)<0JVX;=%kL=mhCDhHEGgpQQj8D+`(SkjFyJ0}x>xWS%K1Hj1n8X%x=o+Zm zIIU4T4v*u`Kf!qZW}ZLAArB~QZZIzWooK{E zaOMQNe)lnOzLt|%?xE2roH>0GX0rDPs#F(hxwlIzD#daLXb4Lo%n`m&(c#tnd$^oD z=TwTOL^>Tu-%wEC%*FGk+E(YcwSLr)pqH-Qgl5}{Z3#H1Ef|ePGv>`BkzN=cR6(!R zq1CqhxWK??FJ9lrufqZELKsy>ZQptD#0#0FM_>&f-oBDyjFrfb4yds5`HK(3t1|oO z*44c0ubWoZN%U!SO&qi9FImccoy9e?*=j3uT6LnfY$z69o!;;c7>N#<$B)FaArZ;M z;hGFuB!v}{mj)u + + + + + diff --git a/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/Contents.json b/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/Contents.json new file mode 100644 index 0000000..a40d882 --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "MTN Logo.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/MTN Logo.svg b/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/MTN Logo.svg new file mode 100644 index 0000000..49cc77f --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/mtnLogo.imageset/MTN Logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Contents.json b/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Contents.json new file mode 100644 index 0000000..f63d95f --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Telecel-Icon.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Telecel-Icon.svg b/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Telecel-Icon.svg new file mode 100644 index 0000000..fac35ee --- /dev/null +++ b/Sources/PaystackUI/Images/Images.xcassets/vodLogo.imageset/Telecel-Icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + +