diff --git a/FRW.xcodeproj/project.pbxproj b/FRW.xcodeproj/project.pbxproj index 965f78c3c..622efba32 100644 --- a/FRW.xcodeproj/project.pbxproj +++ b/FRW.xcodeproj/project.pbxproj @@ -1542,6 +1542,10 @@ 4E722F362ED9A96600627AAD /* EOAPrivateKeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E722F342ED9A96600627AAD /* EOAPrivateKeyView.swift */; }; 4E722F382ED9AF8B00627AAD /* LinkedAccountDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E722F372ED9AF8B00627AAD /* LinkedAccountDetailViewModel.swift */; }; 4E722F392ED9AF8B00627AAD /* LinkedAccountDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E722F372ED9AF8B00627AAD /* LinkedAccountDetailViewModel.swift */; }; + 4E73FAC12F2A378200AF2711 /* EarnButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73FAC02F2A378200AF2711 /* EarnButton.swift */; }; + 4E73FAC22F2A378200AF2711 /* EarnButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73FAC02F2A378200AF2711 /* EarnButton.swift */; }; + 4E73FAC42F2A3C1200AF2711 /* EarnBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73FAC32F2A3C1200AF2711 /* EarnBanner.swift */; }; + 4E73FAC52F2A3C1200AF2711 /* EarnBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73FAC32F2A3C1200AF2711 /* EarnBanner.swift */; }; 4E7712E12E3B2AD70011C1DA /* RNBridgeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7712E02E3B2AD70011C1DA /* RNBridgeError.swift */; }; 4E7712E22E3B2AD70011C1DA /* RNBridgeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7712E02E3B2AD70011C1DA /* RNBridgeError.swift */; }; 4E777CAE2B1ECF8900300A3E /* SyncInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E777CAD2B1ECF8900300A3E /* SyncInfo.swift */; }; @@ -3185,6 +3189,8 @@ 4E7153232E97564D00C5D0BA /* RouteMap+Dev.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RouteMap+Dev.swift"; sourceTree = ""; }; 4E722F342ED9A96600627AAD /* EOAPrivateKeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EOAPrivateKeyView.swift; sourceTree = ""; }; 4E722F372ED9AF8B00627AAD /* LinkedAccountDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedAccountDetailViewModel.swift; sourceTree = ""; }; + 4E73FAC02F2A378200AF2711 /* EarnButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EarnButton.swift; sourceTree = ""; }; + 4E73FAC32F2A3C1200AF2711 /* EarnBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EarnBanner.swift; sourceTree = ""; }; 4E7712E02E3B2AD70011C1DA /* RNBridgeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNBridgeError.swift; sourceTree = ""; }; 4E777CAD2B1ECF8900300A3E /* SyncInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncInfo.swift; sourceTree = ""; }; 4E777CB02B1EE69E00300A3E /* WalletConnect+SyncDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WalletConnect+SyncDevice.swift"; sourceTree = ""; }; @@ -4117,6 +4123,8 @@ 15362A172DA02BA1008F4E51 /* VisualEffectView.swift */, 15362A1A2DA02BCD008F4E51 /* CoinCell.swift */, 4EB2C3092DC0E2F900E79FC5 /* FilterButton.swift */, + 4E73FAC02F2A378200AF2711 /* EarnButton.swift */, + 4E73FAC32F2A3C1200AF2711 /* EarnBanner.swift */, ); path = View; sourceTree = ""; @@ -8532,6 +8540,7 @@ 153749082E26FDD60040E3F8 /* SPPopoverController.swift in Sources */, 153749092E26FDD60040E3F8 /* SPDimmedButton+Colorise.swift in Sources */, 1537490A2E26FDD60040E3F8 /* UISwitchExtension.swift in Sources */, + 4E73FAC52F2A3C1200AF2711 /* EarnBanner.swift in Sources */, 1537490B2E26FDD60040E3F8 /* UIFontExtension.swift in Sources */, 1537490C2E26FDD60040E3F8 /* SPCollectionViewController.swift in Sources */, 1537490D2E26FDD60040E3F8 /* CollectionExtension.swift in Sources */, @@ -9130,6 +9139,7 @@ 4ED4831B2DA515FC00AA23E6 /* BlocklistHandler.swift in Sources */, 6A06CB1128AA54A600C81BE1 /* NFTUIKitGridStyleHandler.swift in Sources */, 1586370D2D6BF1EC0083A068 /* TokenBalanceListView.swift in Sources */, + 4E73FAC22F2A378200AF2711 /* EarnButton.swift in Sources */, 6AC476ED28F40E50008503E6 /* BrowserBookmarkView.swift in Sources */, 4E1A8B702B71A7FA00485EDE /* MultiBackupConfirmPinViewModel.swift in Sources */, 6A8FA4B4285B192900EE5BE3 /* ScrollView+SnappableModifier.swift in Sources */, @@ -9633,6 +9643,7 @@ 153749602E26FDD60040E3F8 /* SPPopoverController.swift in Sources */, 153749612E26FDD60040E3F8 /* SPDimmedButton+Colorise.swift in Sources */, 153749622E26FDD60040E3F8 /* UISwitchExtension.swift in Sources */, + 4E73FAC42F2A3C1200AF2711 /* EarnBanner.swift in Sources */, 153749632E26FDD60040E3F8 /* UIFontExtension.swift in Sources */, 153749642E26FDD60040E3F8 /* SPCollectionViewController.swift in Sources */, 153749652E26FDD60040E3F8 /* CollectionExtension.swift in Sources */, @@ -10231,6 +10242,7 @@ 15C58BAD2868A4EE00BD4FC6 /* ScrollView+SnappableModifier.swift in Sources */, 4ED4831A2DA515FC00AA23E6 /* BlocklistHandler.swift in Sources */, 6AC476EC28F40E50008503E6 /* BrowserBookmarkView.swift in Sources */, + 4E73FAC12F2A378200AF2711 /* EarnButton.swift in Sources */, 1586370C2D6BF1EC0083A068 /* TokenBalanceListView.swift in Sources */, 15C58BAE2868A4EE00BD4FC6 /* VNavigationViewNavigationBar.swift in Sources */, 4E1A8B6F2B71A7FA00485EDE /* MultiBackupConfirmPinViewModel.swift in Sources */, diff --git a/FRW/Foundation/Define/AppUrl.swift b/FRW/Foundation/Define/AppUrl.swift index b1af6609e..36512b757 100644 --- a/FRW/Foundation/Define/AppUrl.swift +++ b/FRW/Foundation/Define/AppUrl.swift @@ -10,4 +10,5 @@ import Foundation enum AppUrl { static let cadenceSwapUrl = "https://app.increment.fi" static let evmSwapUrl = "https://swap.flow.com" + static let earnUrl = "" //TODO: #six 3.1.1 } diff --git a/FRW/Modules/Wallet/TokenDetail/TokenDetailView.swift b/FRW/Modules/Wallet/TokenDetail/TokenDetailView.swift index a713fcdc9..c2723a369 100644 --- a/FRW/Modules/Wallet/TokenDetail/TokenDetailView.swift +++ b/FRW/Modules/Wallet/TokenDetail/TokenDetailView.swift @@ -42,6 +42,10 @@ struct TokenDetailView: RouteableView { .visibility(showAccessibleWarning() ? .visible : .gone) summaryView + if vm.showEarnBanner { + EarnBanner() + } + unverifiedTokenView .visibility(vm.token.isVerifiedValue ? .gone : .visible) diff --git a/FRW/Modules/Wallet/TokenDetail/TokenDetailViewModel.swift b/FRW/Modules/Wallet/TokenDetail/TokenDetailViewModel.swift index 45e44c0ff..c56eaf42f 100644 --- a/FRW/Modules/Wallet/TokenDetail/TokenDetailViewModel.swift +++ b/FRW/Modules/Wallet/TokenDetail/TokenDetailViewModel.swift @@ -184,7 +184,9 @@ class TokenDetailViewModel: ObservableObject { var showStorageView: Bool { token.isFlowCoin && wallet.selectedAccount?.type == .main } var isTokenDetailsButtonEnabled: Bool { token.website.isNotNullNorEmpty } - + + @Published + var showEarnBanner: Bool = !AppUrl.earnUrl.isEmpty // MARK: Private private var cancelSets = Set() diff --git a/FRW/Modules/Wallet/View/EarnBanner.swift b/FRW/Modules/Wallet/View/EarnBanner.swift new file mode 100644 index 000000000..2c9a06de7 --- /dev/null +++ b/FRW/Modules/Wallet/View/EarnBanner.swift @@ -0,0 +1,63 @@ +// +// EarnBanner.swift +// FRW +// +// Created by cat on 28/1/26. +// + +import SwiftUI + +struct EarnBanner: View { + var body: some View { + Button{ + if let url = URL(string: AppUrl.earnUrl) { + Router.route(to: RouteMap.Explore.browser(url)) + } else { + HUD.error(title: "don't have earn url") + } + } label: { + HStack(alignment: .center, spacing: 0) { + VStack(alignment: .leading, spacing: 4) { + Text("Don’t miss out on $1,203 this year") + .font(.inter(size: 14)) + .lineLimit(1) + .foregroundStyle(Color.white) + + Text("Earn 15% APY on Flow Vaults") + .font(.inter(size: 16, weight: .semibold)) + .lineLimit(1) + .foregroundStyle(Color.Brain.Primary.main) + } + Spacer() + Image("earn_icon_36") + .resizable() + .frame(width: 20, height: 20) + .padding(8) + .background(Color.Brain.Primary.main) + .clipShape(Circle()) + } + .padding(18) + .frame(width: .infinity, alignment: .leading) + .background( + LinearGradient( + stops: [ + Gradient.Stop(color: Color(red: 0.1, green: 0.1, blue: 0.1), location: 0.00), + Gradient.Stop(color: Color(red: 0, green: 0.94, blue: 0.55), location: 1.00), + ], + startPoint: UnitPoint(x: 0.75, y: 1), + endPoint: UnitPoint(x: 0.75, y: -5) + ) + ) + .cornerRadius(16) + .overlay( + RoundedRectangle(cornerRadius: 16) + .inset(by: 0.25) + .stroke(Color(red: 0.09, green: 1, blue: 0.6), lineWidth: 0.5) + ) + } + } +} + +#Preview { + EarnBanner() +} diff --git a/FRW/Modules/Wallet/View/EarnButton.swift b/FRW/Modules/Wallet/View/EarnButton.swift new file mode 100644 index 000000000..e16a9b2aa --- /dev/null +++ b/FRW/Modules/Wallet/View/EarnButton.swift @@ -0,0 +1,37 @@ +// +// EarnButton.swift +// FRW +// +// Created by cat on 28/1/26. +// + +import SwiftUI + +struct EarnButton: View { + var body: some View { + Button { + if let url = URL(string: AppUrl.earnUrl) { + Router.route(to: RouteMap.Explore.browser(url)) + } else { + HUD.error(title: "don't have earn url") + } + } label: { + HStack(spacing: 10) { + Text("earn".localized) + .font(.inter(size: 16, weight: .medium)) + .foregroundStyle(Color.Brain.Core.icons) + Image("earn_icon") + .resizable() + .frame(width: 20, height: 20) + } + .frame(height: 40) + .padding(.horizontal, 12) + .background(Color.Brain.Light.lines10) + .cornerRadius(20) + } + } +} + +#Preview { + EarnButton() +} diff --git a/FRW/Modules/Wallet/WalletHomeView.swift b/FRW/Modules/Wallet/WalletHomeView.swift index 51348e874..ee95c6922 100644 --- a/FRW/Modules/Wallet/WalletHomeView.swift +++ b/FRW/Modules/Wallet/WalletHomeView.swift @@ -452,7 +452,11 @@ struct WalletHomeView: View { .font(.inter(size: 18, weight: .bold)) .foregroundStyle(Color.Theme.Text.black3) Spacer() - + + if vm.showEarnButton { + EarnButton() + } + CircleButton(image: .menu) { vm.onClickManagerToken() } diff --git a/FRW/Modules/Wallet/WalletViewModel.swift b/FRW/Modules/Wallet/WalletViewModel.swift index d251225d1..37631dcec 100644 --- a/FRW/Modules/Wallet/WalletViewModel.swift +++ b/FRW/Modules/Wallet/WalletViewModel.swift @@ -184,7 +184,10 @@ final class WalletViewModel: ObservableObject { @Published var walletAccount: WalletAccount? = nil - + + @Published + var showEarnButton: Bool = !AppUrl.earnUrl.isEmpty + var needShowPlaceholder: Bool { isMock || walletState == .noAddress } diff --git a/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/Contents.json b/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/Contents.json new file mode 100644 index 000000000..b66e4fc72 --- /dev/null +++ b/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "earn_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/earn_icon.svg b/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/earn_icon.svg new file mode 100644 index 000000000..5e9f88687 --- /dev/null +++ b/FRW/Resource/Assets.xcassets/wallet/earn_icon.imageset/earn_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/Contents.json b/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/Contents.json new file mode 100644 index 000000000..7a045a698 --- /dev/null +++ b/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "earn_icon_36.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/earn_icon_36.pdf b/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/earn_icon_36.pdf new file mode 100644 index 000000000..b1b91651b Binary files /dev/null and b/FRW/Resource/Assets.xcassets/wallet/earn_icon_36.imageset/earn_icon_36.pdf differ