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

Feat/premium subscription #378

Merged
merged 53 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
4797fe0
fixing merge conflicts - AccountViewModel and SettingsView
timc-mozilla Feb 21, 2023
be167a2
feat(premium): settings premium subscription
timc-mozilla Jan 17, 2023
a54304b
feat(premium): settings premium subscription
timc-mozilla Jan 18, 2023
a714a4a
feat(premium): settings premium subscription
timc-mozilla Jan 19, 2023
85c7188
fixing failed test
timc-mozilla Jan 25, 2023
cf61c80
feat(premium): tos and privacy links
timc-mozilla Feb 7, 2023
8d70fdc
updated struct for no forced unwrap
timc-mozilla Feb 9, 2023
a2b6faa
feat(premium): privacy policy and ToS links
timc-mozilla Feb 9, 2023
349cf7d
feat(premium): privacy policy and ToS links
timc-mozilla Feb 14, 2023
7771145
chore: add missing assets to ImageAsset.swift
Gio2018 Feb 14, 2023
b63810b
feat(premium): update Info.plist, Keys.swift; add alpha premium subsc…
Gio2018 Feb 14, 2023
32c765a
feat(premium): add PremiumSubscription, PremiumSubscriptionStore types
Gio2018 Feb 14, 2023
3a230d2
feat(premium): add PremiumUpgradeViewModel, update AccountViewModel t…
Gio2018 Feb 14, 2023
3c986d5
feat(premium): update PremiumUpgradeView, use PremiumUpgradeViewModel…
Gio2018 Feb 14, 2023
b896535
fix(tests): fix unit tests
Gio2018 Feb 15, 2023
a63c5f3
chore(.gitignore): update .gitignore
Gio2018 Feb 17, 2023
90fa093
feat(premium): update premium keys naming and config
Gio2018 Feb 17, 2023
bc4c067
localizable merge conflicts
timc-mozilla Feb 21, 2023
73c3cb0
fixing string conflicts
timc-mozilla Feb 21, 2023
69600ca
feat(premium): fix typo
Gio2018 Feb 17, 2023
7029245
fixing image asset conflicts
timc-mozilla Feb 21, 2023
8ef9db5
image asset conflicts
timc-mozilla Feb 21, 2023
e36b69d
feat(premium-subscription): premium row image
timc-mozilla Feb 17, 2023
902bebb
reverting auto package changes
timc-mozilla Feb 17, 2023
5c47a39
settings conflicts
timc-mozilla Feb 21, 2023
d8f4697
merge conflicts
timc-mozilla Feb 21, 2023
a866d0d
updated code to run post merge
timc-mozilla Feb 21, 2023
560b9a1
feat(settings): add go premium row for free users
Nov 17, 2022
a27d3c2
feat(premium): update PremiumUpgradeView, add actual prices in the de…
Gio2018 Feb 17, 2023
8880f33
feat(premium): subscription success screen
timc-mozilla Feb 17, 2023
abda53c
Revert "feat(premium): subscription success screen"
timc-mozilla Feb 17, 2023
092bcc4
feat(premium): add StoreKit configuration file
Gio2018 Feb 17, 2023
a76d719
fix(premium): remove duplicate declaration of PremiumUpgradeView
Gio2018 Feb 21, 2023
e15e3d8
fix(assets): remove duplicate declaration in ImageAsset.swift
Gio2018 Feb 21, 2023
8d3df7a
feat(premium): update PremiumUpgradeViewModel, add subscription purch…
Gio2018 Feb 22, 2023
107bae4
feat(premium): update User, refactor protocol and concrete class to p…
Gio2018 Feb 22, 2023
22aa1b3
feat(premium): update PremiumUpgradeView, add purchase action to butt…
Gio2018 Feb 22, 2023
41249d3
feat(premium): update Services, add PremiumSubscriptionStore
Gio2018 Feb 22, 2023
170a911
feat(premium): update AccountViewModel, remove unused import
Gio2018 Feb 23, 2023
be53d89
feat(premium): update unit tests
Gio2018 Feb 23, 2023
c29b78b
feat(premium): subscription success screen
timc-mozilla Feb 24, 2023
d563650
fix(tests): fix unit test after refactoring User
Gio2018 Feb 25, 2023
86c3546
fix lint error
Gio2018 Feb 25, 2023
445750d
feat(premium): update PocketSceneDelegate, inject PremiumUpgradeViewM…
Gio2018 Feb 24, 2023
2734477
feat(premium): update SearchView, SearchViewModel, inject PremiumUpgr…
Gio2018 Feb 24, 2023
42a9c6c
feat(premium): update EmptyStateView, allow to use an external access…
Gio2018 Feb 25, 2023
cc6012e
feat(premium): change get pocket premium button color in search all i…
Gio2018 Feb 25, 2023
5aeaf74
feat(pemium): update Localizable.strings, Strings.swift: add/update d…
Gio2018 Feb 25, 2023
e6806d1
feat(premium): update AccountViewModel, listen for user status changes
Gio2018 Feb 25, 2023
8579b3e
feat(premium): update SettingsRowButton, SettingsView, handle cases o…
Gio2018 Feb 25, 2023
418f3fb
resolve merge conflicts
Gio2018 Feb 27, 2023
9d06f61
feat(premium): add flag to disable the purchase flow
Gio2018 Feb 28, 2023
8c1a179
fix(strings): add missing localized strings
Gio2018 Feb 28, 2023
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ iOSInjectionProject/

# End of https://www.toptal.com/developers/gitignore/api/macos,swift

Config/secrets.xcconfig
Config/secrets*.xcconfig
**/*.otf
**/.swiftpm
**/.tmp
Expand Down
77 changes: 77 additions & 0 deletions Config/Test_Subscriptions.storekit
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"identifier" : "C203F756",
"nonRenewingSubscriptions" : [

],
"products" : [

],
"settings" : {

},
"subscriptionGroups" : [
{
"id" : "7BB42CF1",
"localizations" : [

],
"name" : "Pocket Premium Alpha",
"subscriptions" : [
{
"adHocOffers" : [

],
"codeOffers" : [

],
"displayPrice" : "4.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "EAB5EDEA",
"introductoryOffer" : null,
"localizations" : [
{
"description" : "",
"displayName" : "",
"locale" : "en_US"
}
],
"productID" : "monthly.subscription.pocket",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Pocket Premium Monthly",
"subscriptionGroupID" : "7BB42CF1",
"type" : "RecurringSubscription"
},
{
"adHocOffers" : [

],
"codeOffers" : [

],
"displayPrice" : "44.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "B7F87368",
"introductoryOffer" : null,
"localizations" : [
{
"description" : "",
"displayName" : "",
"locale" : "en_US"
}
],
"productID" : "annual.subscription.pocket",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Pocket Premium Annual",
"subscriptionGroupID" : "7BB42CF1",
"type" : "RecurringSubscription"
}
]
}
],
"version" : {
"major" : 2,
"minor" : 0
}
}
4 changes: 4 additions & 0 deletions Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<string>$(POCKET_API_BASE_URL)</string>
<key>PocketAPIConsumerKey</key>
<string>$(POCKET_API_CONSUMER_KEY)</string>
<key>PocketPremiumMonthly</key>
<string>$(POCKET_PREMIUM_MONTHLY)</string>
<key>PocketPremiumAnnual</key>
<string>$(POCKET_PREMIUM_ANNUAL)</string>
<key>SentryDSN</key>
<string>$(SENTRY_DSN)</string>
<key>SnowplowEndpoint</key>
Expand Down
12 changes: 10 additions & 2 deletions Pocket.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
8A7765A2288EE80900127BB4 /* RecentSavesCellElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7765A1288EE80900127BB4 /* RecentSavesCellElement.swift */; };
8A8F5EFE28CB740000124B6D /* EditTagsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8F5EFD28CB740000124B6D /* EditTagsTests.swift */; };
8ADAC6B028AD4E7500DE9A62 /* AddTagsViewElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ADAC6AF28AD4E7500DE9A62 /* AddTagsViewElement.swift */; };
AA7D6A2B2995F0A20094FD18 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA7D6A2A2995F0A20094FD18 /* StoreKit.framework */; };
8ADD6A9F292C189C007F419D /* SearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ADD6A9E292C189C007F419D /* SearchTests.swift */; };
8ADD6AA1292C1B2C007F419D /* SearchViewElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ADD6AA0292C1B2C007F419D /* SearchViewElement.swift */; };
D26A5EF4297F1D8400FA5A88 /* ReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26A5EF3297F1D8400FA5A88 /* ReaderTests.swift */; };
Expand Down Expand Up @@ -215,9 +216,12 @@
8A7765A1288EE80900127BB4 /* RecentSavesCellElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentSavesCellElement.swift; sourceTree = "<group>"; };
8A8F5EFD28CB740000124B6D /* EditTagsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTagsTests.swift; sourceTree = "<group>"; };
8ADAC6AF28AD4E7500DE9A62 /* AddTagsViewElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTagsViewElement.swift; sourceTree = "<group>"; };
AA7D6A2A2995F0A20094FD18 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.2.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
D26A5EF3297F1D8400FA5A88 /* ReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderTests.swift; sourceTree = "<group>"; };
8ADD6A9E292C189C007F419D /* SearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTests.swift; sourceTree = "<group>"; };
8ADD6AA0292C1B2C007F419D /* SearchViewElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewElement.swift; sourceTree = "<group>"; };
D26A5EF3297F1D8400FA5A88 /* ReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderTests.swift; sourceTree = "<group>"; };
AA909AC8299EFD3A00F90FA7 /* secrets_test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = secrets_test.xcconfig; sourceTree = "<group>"; };
AA909ACC299F4D5400F90FA7 /* Test_Subscriptions.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Test_Subscriptions.storekit; sourceTree = "<group>"; };
F204A76027E22EC50010E155 /* SaveToPocket.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SaveToPocket.appex; sourceTree = BUILT_PRODUCTS_DIR; };
F204A76727E22EC50010E155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F20BB0382744542F00AE5E70 /* AlertElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertElement.swift; sourceTree = "<group>"; };
Expand All @@ -238,6 +242,7 @@
buildActionMask = 2147483647;
files = (
16BA7D6626851579009A17C1 /* PocketKit in Frameworks */,
AA7D6A2B2995F0A20094FD18 /* StoreKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -410,7 +415,9 @@
16973354263CBB290003DE2A /* Config */ = {
isa = PBXGroup;
children = (
AA909AC8299EFD3A00F90FA7 /* secrets_test.xcconfig */,
16973355263CBB3F0003DE2A /* secrets.xcconfig */,
AA909ACC299F4D5400F90FA7 /* Test_Subscriptions.storekit */,
);
path = Config;
sourceTree = "<group>";
Expand Down Expand Up @@ -449,6 +456,7 @@
F220F2B8264DC2750064D272 /* Frameworks */ = {
isa = PBXGroup;
children = (
AA7D6A2A2995F0A20094FD18 /* StoreKit.framework */,
F227F0C3265E96290031F985 /* CoreText.framework */,
65EC272628BA8AD50075E1DF /* UserNotifications.framework */,
65EC272828BA8AD50075E1DF /* UserNotificationsUI.framework */,
Expand Down Expand Up @@ -1069,7 +1077,7 @@
};
1676E5A727FBC89A00F9283A /* Debug_Test */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 16973355263CBB3F0003DE2A /* secrets.xcconfig */;
baseConfigurationReference = AA909AC8299EFD3A00F90FA7 /* secrets_test.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
Expand Down
12 changes: 12 additions & 0 deletions PocketKit/Sources/PocketKit/Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ struct Keys {
let sentryDSN: String
let brazeAPIEndpoint: String
let brazeAPIKey: String
let pocketPremiumMonthly: String
let pocketPremiumAnnual: String

private init() {
guard let info = Bundle.main.infoDictionary else {
Expand All @@ -33,9 +35,19 @@ struct Keys {
fatalError("Unable to extract BrazeAPIKey from main bundle")
}

guard let pocketPremiumMonthly = info["PocketPremiumMonthly"] as? String else {
fatalError("Unable to extract PocketPremiumMonthlyAlpha from main bundle")
}

guard let pocketPremiumAnnual = info["PocketPremiumAnnual"] as? String else {
fatalError("Unable to extract PocketPremiumAnnualAlpha from main bundle")
}

self.pocketApiConsumerKey = pocketApiConsumerKey
self.sentryDSN = sentryDSN
self.brazeAPIEndpoint = brazeAPIEndpoint
self.brazeAPIKey = brazeAPIKey
self.pocketPremiumMonthly = pocketPremiumMonthly
self.pocketPremiumAnnual = pocketPremiumAnnual
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ open class SwiftUICollectionViewCell<Content>: UICollectionViewCell where Conten
}
}

class EmptyStateCollectionViewCell: SwiftUICollectionViewCell<EmptyStateView> {
class EmptyStateCollectionViewCell: SwiftUICollectionViewCell<EmptyStateView<EmptyView>> {
func configure(parent: UIViewController, _ viewModel: EmptyStateViewModel) {
embed(in: parent, withView: EmptyStateView(viewModel: viewModel))
host?.view.frame = self.contentView.bounds
Expand All @@ -34,14 +34,16 @@ class EmptyStateCollectionViewCell: SwiftUICollectionViewCell<EmptyStateView> {
}
}

struct EmptyStateView: View {
private var viewModel: EmptyStateViewModel
struct EmptyStateView<Content: View>: View {
private let viewModel: EmptyStateViewModel
private var content: Content?

@State
private var showSafariView = false

init(viewModel: EmptyStateViewModel) {
init(viewModel: EmptyStateViewModel, content: (() -> Content)? = nil) {
self.viewModel = viewModel
self.content = content?()
}

var body: some View {
Expand All @@ -64,8 +66,9 @@ struct EmptyStateView: View {
}
} else { Text(subtitle).style(.detail) }
}

if let buttonText = viewModel.buttonText, let webURL = viewModel.webURL {
if let content {
content
} else if let buttonText = viewModel.buttonText, let webURL = viewModel.webURL {
Button(action: {
self.showSafariView = true
}, label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct SearchView: View {
switch viewModel.searchState {
case .emptyState(let emptyStateViewModel):
SearchEmptyView(viewModel: emptyStateViewModel)
.environmentObject(viewModel)
case .recentSearches(let searches):
RecentSearchView(viewModel: viewModel, recentSearches: searches)
case .searchResults(let results):
Expand All @@ -23,8 +24,6 @@ struct SearchView: View {
default:
EmptyView()
}
}.onAppear {
viewModel.trackOpenSearch()
}
}
}
Expand Down Expand Up @@ -68,11 +67,45 @@ struct ResultsView: View {

// MARK: - Search Empty States Component
struct SearchEmptyView: View {
var viewModel: EmptyStateViewModel
private var viewModel: EmptyStateViewModel

init(viewModel: EmptyStateViewModel) {
self.viewModel = viewModel
}

var body: some View {
EmptyStateView(viewModel: viewModel)
if let text = viewModel.buttonText {
EmptyStateView(viewModel: viewModel) {
GetPocketPremiumButton(text: text)
}
.padding(Margins.normal.rawValue)
} else {
EmptyStateView<EmptyView>(viewModel: viewModel)
.padding(Margins.normal.rawValue)
}
}
}

struct GetPocketPremiumButton: View {
@EnvironmentObject private var searchViewModel: SearchViewModel
private let text: String

init(text: String) {
self.text = text
}

var body: some View {
Button(action: {
searchViewModel.showPremiumUpgrade()
}, label: {
Text(text)
.style(.header.sansSerif.h7.with(color: .ui.white))
.padding(EdgeInsets(top: 12, leading: 0, bottom: 12, trailing: 0))
.frame(maxWidth: 320)
}).buttonStyle(GetPocketPremiumButtonStyle())
.sheet(isPresented: $searchViewModel.isPresentingPremiumUpgrade) {
PremiumUpgradeView(viewModel: searchViewModel.makePremiumUpgradeViewModel())
}
}
}

Expand Down
Loading