From 975abf4ea02e4db38e78265fe30687fc8c527ae4 Mon Sep 17 00:00:00 2001 From: Tobias Strebitzer Date: Thu, 11 Jun 2015 11:30:53 +0200 Subject: [PATCH] storyboard implementation and various UI bug fixes --- Baker.xcodeproj/project.pbxproj | 107 ++- Baker/BKRAppDelegate.h | 4 +- Baker/BKRAppDelegate.m | 50 +- Baker/Baker-Info.plist | 2 + Baker/icon-buy.png | Bin 0 -> 494 bytes Baker/icon-buy@2x.png | Bin 0 -> 565 bytes Baker/icon-buy@3x.png | Bin 0 -> 558 bytes Baker/icon-categories.png | Bin 0 -> 424 bytes Baker/icon-categories@2x.png | Bin 0 -> 515 bytes Baker/icon-categories@3x.png | Bin 0 -> 326 bytes Baker/icon-info.png | Bin 0 -> 598 bytes Baker/icon-info@2x.png | Bin 0 -> 1016 bytes Baker/icon-info@3x.png | Bin 0 -> 1375 bytes Baker/icon-refresh.png | Bin 0 -> 586 bytes Baker/icon-refresh@2x.png | Bin 0 -> 893 bytes Baker/icon-refresh@3x.png | Bin 0 -> 1259 bytes Baker/icon-subscribe.png | Bin 0 -> 562 bytes Baker/icon-subscribe@2x.png | Bin 0 -> 882 bytes Baker/icon-subscribe@3x.png | Bin 0 -> 1266 bytes ...FilterItem.h => BKRCategoryFilterButton.h} | 13 +- ...FilterItem.m => BKRCategoryFilterButton.m} | 35 +- BakerShelf/BKRCustomNavigationController.h | 2 + BakerShelf/BKRCustomNavigationController.m | 22 + BakerShelf/BKRIssue.h | 37 +- BakerShelf/BKRIssue.m | 61 +- ...KRIssueViewController.h => BKRIssueCell.h} | 76 +- BakerShelf/BKRIssueCell.m | 208 +++++ BakerShelf/BKRIssueViewController.m | 649 ---------------- BakerShelf/BKRIssuesManager.h | 2 + BakerShelf/BKRIssuesManager.m | 18 + BakerShelf/BKRPurchasesManager.h | 19 + BakerShelf/BKRPurchasesManager.m | 48 +- BakerShelf/BKRShelfViewController.h | 41 +- BakerShelf/BKRShelfViewController.m | 718 ++++++++---------- BakerView/BKRBookViewController.h | 14 +- BakerView/BKRBookViewController.m | 175 ++--- BakerView/BKRShelfViewLayout.m | 11 +- BakerView/ui/BKRStoryboard.storyboard | 364 +++++++++ 38 files changed, 1310 insertions(+), 1366 deletions(-) create mode 100644 Baker/icon-buy.png create mode 100644 Baker/icon-buy@2x.png create mode 100755 Baker/icon-buy@3x.png create mode 100644 Baker/icon-categories.png create mode 100644 Baker/icon-categories@2x.png create mode 100755 Baker/icon-categories@3x.png create mode 100644 Baker/icon-info.png create mode 100644 Baker/icon-info@2x.png create mode 100755 Baker/icon-info@3x.png create mode 100644 Baker/icon-refresh.png create mode 100644 Baker/icon-refresh@2x.png create mode 100755 Baker/icon-refresh@3x.png create mode 100644 Baker/icon-subscribe.png create mode 100644 Baker/icon-subscribe@2x.png create mode 100755 Baker/icon-subscribe@3x.png rename BakerComponents/{BKRCategoryFilterItem.h => BKRCategoryFilterButton.h} (84%) rename BakerComponents/{BKRCategoryFilterItem.m => BKRCategoryFilterButton.m} (79%) rename BakerShelf/{BKRIssueViewController.h => BKRIssueCell.h} (53%) create mode 100644 BakerShelf/BKRIssueCell.m delete mode 100644 BakerShelf/BKRIssueViewController.m create mode 100644 BakerView/ui/BKRStoryboard.storyboard diff --git a/Baker.xcodeproj/project.pbxproj b/Baker.xcodeproj/project.pbxproj index e91fe759..1598aaf2 100644 --- a/Baker.xcodeproj/project.pbxproj +++ b/Baker.xcodeproj/project.pbxproj @@ -9,7 +9,24 @@ /* Begin PBXBuildFile section */ 20CC551D16AD346900DEFB5C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 20CC551F16AD346900DEFB5C /* Localizable.strings */; }; 904033901A10FF230035071A /* BKRShelfViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 9040338F1A10FF230035071A /* BKRShelfViewLayout.m */; }; - 904C31E71A12645C00E53E66 /* BKRCategoryFilterItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 904C31E61A12645C00E53E66 /* BKRCategoryFilterItem.m */; }; + 904C31E71A12645C00E53E66 /* BKRCategoryFilterButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 904C31E61A12645C00E53E66 /* BKRCategoryFilterButton.m */; }; + 90F4908A1B26FDDD0010A4D9 /* BKRStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90F490891B26FDDD0010A4D9 /* BKRStoryboard.storyboard */; }; + 90F4908D1B26FDF00010A4D9 /* BKRIssueCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 90F4908C1B26FDF00010A4D9 /* BKRIssueCell.m */; }; + 90F490B81B2718B70010A4D9 /* icon-categories.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490AC1B2718B70010A4D9 /* icon-categories.png */; }; + 90F490B91B2718B70010A4D9 /* icon-categories@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490AD1B2718B70010A4D9 /* icon-categories@2x.png */; }; + 90F490BA1B2718B70010A4D9 /* icon-categories@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490AE1B2718B70010A4D9 /* icon-categories@3x.png */; }; + 90F490BB1B2718B70010A4D9 /* icon-refresh.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490AF1B2718B70010A4D9 /* icon-refresh.png */; }; + 90F490BC1B2718B70010A4D9 /* icon-refresh@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490B01B2718B70010A4D9 /* icon-refresh@2x.png */; }; + 90F490BD1B2718B70010A4D9 /* icon-refresh@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490B11B2718B70010A4D9 /* icon-refresh@3x.png */; }; + 90F490CA1B271A380010A4D9 /* icon-buy.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C41B271A380010A4D9 /* icon-buy.png */; }; + 90F490CB1B271A380010A4D9 /* icon-buy@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C51B271A380010A4D9 /* icon-buy@2x.png */; }; + 90F490CC1B271A380010A4D9 /* icon-buy@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C61B271A380010A4D9 /* icon-buy@3x.png */; }; + 90F490CD1B271A380010A4D9 /* icon-subscribe.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C71B271A380010A4D9 /* icon-subscribe.png */; }; + 90F490CE1B271A380010A4D9 /* icon-subscribe@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C81B271A380010A4D9 /* icon-subscribe@2x.png */; }; + 90F490CF1B271A380010A4D9 /* icon-subscribe@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490C91B271A380010A4D9 /* icon-subscribe@3x.png */; }; + 90F490D31B271AB00010A4D9 /* icon-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490D01B271AB00010A4D9 /* icon-info.png */; }; + 90F490D41B271AB00010A4D9 /* icon-info@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490D11B271AB00010A4D9 /* icon-info@2x.png */; }; + 90F490D51B271AB00010A4D9 /* icon-info@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 90F490D21B271AB00010A4D9 /* icon-info@3x.png */; }; 9F55576815B4C6C3003072A3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F55576515B4C6B7003072A3 /* QuartzCore.framework */; }; 9F58D85115D66E870027902C /* BKRCustomNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F58D84E15D66E870027902C /* BKRCustomNavigationBar.m */; }; 9F58D85215D66E870027902C /* BKRCustomNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F58D85015D66E870027902C /* BKRCustomNavigationController.m */; }; @@ -40,7 +57,6 @@ ABAFB59C15AB9A2E002FA498 /* BKRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ABAFB59B15AB9A2E002FA498 /* BKRAppDelegate.m */; }; ABC270D11616F3CB00AD97E9 /* BKRIssuesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = ABC270D01616F3CB00AD97E9 /* BKRIssuesManager.m */; }; ABD245531639E3650080F056 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABD245521639E3650080F056 /* AVFoundation.framework */; }; - ABD24C1E1620309B0042E49C /* BKRIssueViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ABD24C1D1620309B0042E49C /* BKRIssueViewController.m */; }; ABD6FC4016544EAD0032BC44 /* NSData+BakerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = ABD6FC3F16544EAD0032BC44 /* NSData+BakerExtensions.m */; }; ABD7D45F1616FC5B008B13B5 /* BKRIssue.m in Sources */ = {isa = PBXBuildFile; fileRef = ABD7D45E1616FC5B008B13B5 /* BKRIssue.m */; }; ABDC05AF16A4C42A00F6B63D /* BKRJSONStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = ABDC05AE16A4C42A00F6B63D /* BKRJSONStatus.m */; }; @@ -71,8 +87,26 @@ 20CC551E16AD346900DEFB5C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 9040338E1A10FF230035071A /* BKRShelfViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRShelfViewLayout.h; sourceTree = ""; }; 9040338F1A10FF230035071A /* BKRShelfViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRShelfViewLayout.m; sourceTree = ""; }; - 904C31E51A12645C00E53E66 /* BKRCategoryFilterItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRCategoryFilterItem.h; sourceTree = ""; }; - 904C31E61A12645C00E53E66 /* BKRCategoryFilterItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRCategoryFilterItem.m; sourceTree = ""; }; + 904C31E51A12645C00E53E66 /* BKRCategoryFilterButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRCategoryFilterButton.h; sourceTree = ""; }; + 904C31E61A12645C00E53E66 /* BKRCategoryFilterButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRCategoryFilterButton.m; sourceTree = ""; }; + 90F490891B26FDDD0010A4D9 /* BKRStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = BKRStoryboard.storyboard; sourceTree = ""; }; + 90F4908B1B26FDF00010A4D9 /* BKRIssueCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRIssueCell.h; sourceTree = ""; }; + 90F4908C1B26FDF00010A4D9 /* BKRIssueCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRIssueCell.m; sourceTree = ""; }; + 90F490AC1B2718B70010A4D9 /* icon-categories.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-categories.png"; sourceTree = ""; }; + 90F490AD1B2718B70010A4D9 /* icon-categories@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-categories@2x.png"; sourceTree = ""; }; + 90F490AE1B2718B70010A4D9 /* icon-categories@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-categories@3x.png"; sourceTree = ""; }; + 90F490AF1B2718B70010A4D9 /* icon-refresh.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-refresh.png"; sourceTree = ""; }; + 90F490B01B2718B70010A4D9 /* icon-refresh@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-refresh@2x.png"; sourceTree = ""; }; + 90F490B11B2718B70010A4D9 /* icon-refresh@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-refresh@3x.png"; sourceTree = ""; }; + 90F490C41B271A380010A4D9 /* icon-buy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-buy.png"; sourceTree = ""; }; + 90F490C51B271A380010A4D9 /* icon-buy@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-buy@2x.png"; sourceTree = ""; }; + 90F490C61B271A380010A4D9 /* icon-buy@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-buy@3x.png"; sourceTree = ""; }; + 90F490C71B271A380010A4D9 /* icon-subscribe.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-subscribe.png"; sourceTree = ""; }; + 90F490C81B271A380010A4D9 /* icon-subscribe@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-subscribe@2x.png"; sourceTree = ""; }; + 90F490C91B271A380010A4D9 /* icon-subscribe@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-subscribe@3x.png"; sourceTree = ""; }; + 90F490D01B271AB00010A4D9 /* icon-info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-info.png"; sourceTree = ""; }; + 90F490D11B271AB00010A4D9 /* icon-info@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-info@2x.png"; sourceTree = ""; }; + 90F490D21B271AB00010A4D9 /* icon-info@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-info@3x.png"; sourceTree = ""; }; 9F55576515B4C6B7003072A3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 9F58D84D15D66E870027902C /* BKRCustomNavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRCustomNavigationBar.h; sourceTree = ""; }; 9F58D84E15D66E870027902C /* BKRCustomNavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRCustomNavigationBar.m; sourceTree = ""; }; @@ -125,8 +159,6 @@ ABC270CF1616F3CB00AD97E9 /* BKRIssuesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRIssuesManager.h; sourceTree = ""; }; ABC270D01616F3CB00AD97E9 /* BKRIssuesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRIssuesManager.m; sourceTree = ""; }; ABD245521639E3650080F056 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; - ABD24C1C1620309B0042E49C /* BKRIssueViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRIssueViewController.h; sourceTree = ""; }; - ABD24C1D1620309B0042E49C /* BKRIssueViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKRIssueViewController.m; sourceTree = ""; }; ABD6FC3E16544EAD0032BC44 /* NSData+BakerExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+BakerExtensions.h"; sourceTree = ""; }; ABD6FC3F16544EAD0032BC44 /* NSData+BakerExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+BakerExtensions.m"; sourceTree = ""; }; ABD7D45D1616FC5B008B13B5 /* BKRIssue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKRIssue.h; sourceTree = ""; }; @@ -197,12 +229,34 @@ 904C31E41A12645C00E53E66 /* BakerComponents */ = { isa = PBXGroup; children = ( - 904C31E51A12645C00E53E66 /* BKRCategoryFilterItem.h */, - 904C31E61A12645C00E53E66 /* BKRCategoryFilterItem.m */, + 904C31E51A12645C00E53E66 /* BKRCategoryFilterButton.h */, + 904C31E61A12645C00E53E66 /* BKRCategoryFilterButton.m */, ); path = BakerComponents; sourceTree = ""; }; + 90F490AB1B2718B10010A4D9 /* Icons */ = { + isa = PBXGroup; + children = ( + 90F490C41B271A380010A4D9 /* icon-buy.png */, + 90F490C51B271A380010A4D9 /* icon-buy@2x.png */, + 90F490C61B271A380010A4D9 /* icon-buy@3x.png */, + 90F490C71B271A380010A4D9 /* icon-subscribe.png */, + 90F490C81B271A380010A4D9 /* icon-subscribe@2x.png */, + 90F490C91B271A380010A4D9 /* icon-subscribe@3x.png */, + 90F490AC1B2718B70010A4D9 /* icon-categories.png */, + 90F490AD1B2718B70010A4D9 /* icon-categories@2x.png */, + 90F490AE1B2718B70010A4D9 /* icon-categories@3x.png */, + 90F490AF1B2718B70010A4D9 /* icon-refresh.png */, + 90F490B01B2718B70010A4D9 /* icon-refresh@2x.png */, + 90F490B11B2718B70010A4D9 /* icon-refresh@3x.png */, + 90F490D01B271AB00010A4D9 /* icon-info.png */, + 90F490D11B271AB00010A4D9 /* icon-info@2x.png */, + 90F490D21B271AB00010A4D9 /* icon-info@3x.png */, + ); + name = Icons; + sourceTree = ""; + }; 9F58D85E15D6743C0027902C /* img */ = { isa = PBXGroup; children = ( @@ -285,6 +339,7 @@ children = ( AB40045115B4B83000D87E2A /* BKRPageTitleLabel.h */, AB40045215B4B83000D87E2A /* BKRPageTitleLabel.m */, + 90F490891B26FDDD0010A4D9 /* BKRStoryboard.storyboard */, ); path = ui; sourceTree = ""; @@ -305,8 +360,8 @@ ABD7D45E1616FC5B008B13B5 /* BKRIssue.m */, ABC270CF1616F3CB00AD97E9 /* BKRIssuesManager.h */, ABC270D01616F3CB00AD97E9 /* BKRIssuesManager.m */, - ABD24C1C1620309B0042E49C /* BKRIssueViewController.h */, - ABD24C1D1620309B0042E49C /* BKRIssueViewController.m */, + 90F4908B1B26FDF00010A4D9 /* BKRIssueCell.h */, + 90F4908C1B26FDF00010A4D9 /* BKRIssueCell.m */, AB2317D2169F808000BE3BE7 /* BKRPurchasesManager.h */, AB2317D3169F808000BE3BE7 /* BKRPurchasesManager.m */, C649F0961A090B6000B02548 /* BKRShelfHeaderView.h */, @@ -361,6 +416,7 @@ ABAFB59115AB9A2E002FA498 /* Baker */ = { isa = PBXGroup; children = ( + 90F490AB1B2718B10010A4D9 /* Icons */, C659B2911A38BA3700CA238C /* newsstand-app-icon.png */, C659B2921A38BA3700CA238C /* newsstand-app-icon@2x.png */, C659B2931A38BA3700CA238C /* newsstand-app-icon@3x.png */, @@ -426,11 +482,6 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 0630; - TargetAttributes = { - ABAFB58615AB9A2E002FA498 = { - DevelopmentTeam = D98EH4D5B2; - }; - }; }; buildConfigurationList = ABAFB58115AB9A2E002FA498 /* Build configuration list for PBXProject "Baker" */; compatibilityVersion = "Xcode 3.2"; @@ -454,14 +505,30 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 90F490D51B271AB00010A4D9 /* icon-info@3x.png in Resources */, + 90F490BA1B2718B70010A4D9 /* icon-categories@3x.png in Resources */, C659B2941A38BA3700CA238C /* newsstand-app-icon.png in Resources */, C659B2951A38BA3700CA238C /* newsstand-app-icon@2x.png in Resources */, C659B2961A38BA3700CA238C /* newsstand-app-icon@3x.png in Resources */, AB40048515B4BB8B00D87E2A /* books in Resources */, + 90F490B81B2718B70010A4D9 /* icon-categories.png in Resources */, BF78D1B0180C8F070093DFBC /* BakerAssets.xcassets in Resources */, + 90F490B91B2718B70010A4D9 /* icon-categories@2x.png in Resources */, + 90F490CC1B271A380010A4D9 /* icon-buy@3x.png in Resources */, + 90F490CA1B271A380010A4D9 /* icon-buy.png in Resources */, + 90F490CF1B271A380010A4D9 /* icon-subscribe@3x.png in Resources */, + 90F490CB1B271A380010A4D9 /* icon-buy@2x.png in Resources */, 20CC551D16AD346900DEFB5C /* Localizable.strings in Resources */, + 90F4908A1B26FDDD0010A4D9 /* BKRStoryboard.storyboard in Resources */, + 90F490D31B271AB00010A4D9 /* icon-info.png in Resources */, BF0785EB1794AF4000EB988E /* info in Resources */, + 90F490D41B271AB00010A4D9 /* icon-info@2x.png in Resources */, + 90F490BD1B2718B70010A4D9 /* icon-refresh@3x.png in Resources */, + 90F490CD1B271A380010A4D9 /* icon-subscribe.png in Resources */, + 90F490BC1B2718B70010A4D9 /* icon-refresh@2x.png in Resources */, + 90F490CE1B271A380010A4D9 /* icon-subscribe@2x.png in Resources */, C649F09D1A0926CD00B02548 /* settings.plist in Resources */, + 90F490BB1B2718B70010A4D9 /* icon-refresh.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -476,7 +543,7 @@ ABAFB59815AB9A2E002FA498 /* main.m in Sources */, ABAFB59C15AB9A2E002FA498 /* BKRAppDelegate.m in Sources */, AB40045315B4B83000D87E2A /* BKRBookViewController.m in Sources */, - 904C31E71A12645C00E53E66 /* BKRCategoryFilterItem.m in Sources */, + 904C31E71A12645C00E53E66 /* BKRCategoryFilterButton.m in Sources */, AB40045F15B4B83000D87E2A /* BKRIndexViewController.m in Sources */, AB40046115B4B83000D87E2A /* BKRInterceptorWindow.m in Sources */, C62D23571A0A748A003A26D2 /* unzip.c in Sources */, @@ -497,7 +564,6 @@ AB26005116011D9D00E9AA66 /* BKRBookStatus.m in Sources */, ABD7D45F1616FC5B008B13B5 /* BKRIssue.m in Sources */, ABC270D11616F3CB00AD97E9 /* BKRIssuesManager.m in Sources */, - ABD24C1E1620309B0042E49C /* BKRIssueViewController.m in Sources */, C62D23551A0A748A003A26D2 /* ioapi.c in Sources */, ABD6FC4016544EAD0032BC44 /* NSData+BakerExtensions.m in Sources */, AB2317D4169F808000BE3BE7 /* BKRPurchasesManager.m in Sources */, @@ -511,6 +577,7 @@ AB67F5BA16EE99B2005A04F6 /* BKRReachability.m in Sources */, C62D23581A0A748A003A26D2 /* zip.c in Sources */, ABF23B1D1708418E003AA3B0 /* BKRBakerAPI.m in Sources */, + 90F4908D1B26FDF00010A4D9 /* BKRIssueCell.m in Sources */, C62D233F1A0A71DC003A26D2 /* BKRAnalyticsEvents.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -545,7 +612,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; + GCC_NO_COMMON_BLOCKS = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -581,7 +648,7 @@ COPY_PHASE_STRIP = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; + GCC_NO_COMMON_BLOCKS = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -603,6 +670,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Baker/Baker-Prefix.pch"; INFOPLIST_FILE = "Baker/Baker-Info.plist"; @@ -619,6 +687,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Baker/Baker-Prefix.pch"; INFOPLIST_FILE = "Baker/Baker-Info.plist"; diff --git a/Baker/BKRAppDelegate.h b/Baker/BKRAppDelegate.h index e4898178..b04629ce 100644 --- a/Baker/BKRAppDelegate.h +++ b/Baker/BKRAppDelegate.h @@ -38,7 +38,7 @@ @interface BKRAppDelegate : UIResponder @property (nonatomic, strong) BKRInterceptorWindow *window; -@property (nonatomic, strong) UIViewController *rootViewController; -@property (nonatomic, strong) UINavigationController *rootNavigationController; +@property (nonatomic, strong) IBOutlet UIViewController *rootViewController; +@property (nonatomic, strong) IBOutlet UINavigationController *rootNavigationController; @end diff --git a/Baker/BKRAppDelegate.m b/Baker/BKRAppDelegate.m index d091af09..ac0b90c9 100644 --- a/Baker/BKRAppDelegate.m +++ b/Baker/BKRAppDelegate.m @@ -54,23 +54,16 @@ + (void)initialize { } - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - if ([BKRSettings sharedSettings].isNewsstand) { [self configureNewsstandApp:application options:launchOptions]; - } else { - [self configureStandAloneApp:application options:launchOptions]; + }else{ + if([[BKRIssuesManager localBooksList] count] == 1) { + self.window.rootViewController = [[BKRBookViewController alloc] initWithIssue:[BKRIssuesManager localBooksList][0]]; + [self.window makeKeyAndVisible]; + } } - - self.rootNavigationController = [[BKRCustomNavigationController alloc] initWithRootViewController:self.rootViewController]; - - [self configureNavigationBar]; - [self configureAnalytics]; - - self.window = [[BKRInterceptorWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - self.window.backgroundColor = [UIColor whiteColor]; - self.window.rootViewController = self.rootNavigationController; - [self.window makeKeyAndVisible]; + [self configureAnalytics]; return YES; } @@ -119,32 +112,9 @@ - (void)configureNewsstandApp:(UIApplication*)application options:(NSDictionary* dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); } } - - self.rootViewController = [[BKRShelfViewController alloc] init]; } -- (void)configureStandAloneApp:(UIApplication*)application options:(NSDictionary*)launchOptions { - - NSLog(@"====== Baker Standalone Mode enabled ======"); - NSArray *books = [BKRIssuesManager localBooksList]; - if (books.count == 1) { - BKRBook *book = [books[0] bakerBook]; - self.rootViewController = [[BKRBookViewController alloc] initWithBook:book]; - } else { - self.rootViewController = [[BKRShelfViewController alloc] initWithBooks:books]; - } - -} - -- (void)configureNavigationBar { - BKRCustomNavigationBar *navigationBar = (BKRCustomNavigationBar*)self.rootNavigationController.navigationBar; - navigationBar.tintColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesActionBackgroundColor]; - navigationBar.barTintColor = [UIColor bkrColorWithHexString:@"ffffff"]; - navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor bkrColorWithHexString:@"000000"]}; - [navigationBar setBackgroundImage:[UIImage imageNamed:@"navigation-bar-bg"] forBarMetrics:UIBarMetricsDefault]; -} - - (void)configureAnalytics { [BKRAnalyticsEvents sharedInstance]; // Initialization [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerApplicationStart" object:self]; // -> Baker Analytics Event @@ -247,6 +217,14 @@ - (void)applicationWillHandleNewsstandNotificationOfContent:(NSString*)contentNa } +- (BKRInterceptorWindow *)window { + static BKRInterceptorWindow *customWindow = nil; + if (!customWindow) { + customWindow = [[BKRInterceptorWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + } + return customWindow; +} + #pragma mark - Application Lifecycle - (void)applicationWillResignActive:(UIApplication*)application { diff --git a/Baker/Baker-Info.plist b/Baker/Baker-Info.plist index dc8b6268..1f9aacb3 100644 --- a/Baker/Baker-Info.plist +++ b/Baker/Baker-Info.plist @@ -43,6 +43,8 @@ newsstand-content remote-notification + UIMainStoryboardFile + BKRStoryboard UINewsstandApp UIRequiredDeviceCapabilities diff --git a/Baker/icon-buy.png b/Baker/icon-buy.png new file mode 100644 index 0000000000000000000000000000000000000000..2f205e7497cbd4ab0fe92c5b27c884b4eba7ac2d GIT binary patch literal 494 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk*)xbzkFcBNmKyy6Sfyo0Uo6BcE(mMOBBAM5M(+@LorGxjSl5A)~!Kkrxn zIebqs`+K8NU}Q9xNW!B_3XT!GilnVW-u3Ryt~$kiTyAc(h(_qWm>DO{r>vhdYt@vA z0<}**^|HM6y;jN5mM1i+ZQf%=p(6E@E{F41%LZ(@752>2^QKi|-XxEg@AMabe6@7> z;e~2KyUaH)`H^FLU-p9Fp|XW;{Eh(mdKI;Vst08sF( Ay8r+H literal 0 HcmV?d00001 diff --git a/Baker/icon-buy@2x.png b/Baker/icon-buy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa9ad724a4247fced5946572986ef0098539ded GIT binary patch literal 565 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-2>S4={E+nQaGTu$OrHy0YJA<7C%0GH_cwi-Cbr!_&nvB;xSf>Gs)&9Aw(&Px&k# zDeA(-)hBzzcfXIl!(W>`ztYc>cXUqH%L`gk#=BNES7uMky{#)}?Y_|UJ$9E^hPLFaIS{D6&fIOUZAg4jyBJ#b2uO)|1h_K9X4%xT{+PlLQGC8@cF(u@TPl~ZPWf<^=TTXE4&RfWPpSV- zen{#4+B0jV)`cgB7r30=u`{!QP5B-#linqZd5xh@?=iFgsBW-x-BuQ;b>N!bFI8si z-5L|+R+n!Ion_K~dcG3-4kw3{H!CbT-Z})R8$^hfzMPSeA?8{F?IH$C3pk>^eP{o?!C8<`) zMX5lF!N|bSRM)^v*T6Ew(8S8b%*wz-+rY@mz~Fqs$$AtGx%nxXX_d$t49u-eOsz}} WAR104Q&rUPlPeujz(>310z7Y8z|sPdDG(d4hYAiYEvy zVG^q=I4@zrQr6IWiCyD~=nm#}4b$)cQq4Y~`hl;;qUKCg48!W!<8sXWwiXHT&*daa zMXOnNygj(^m!0|Mu&L9eSnQeoc2#}NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5SkgylyhcYg>utMMObc5=REIpY*?y`kX zeNI1LcUV`i;jyUlRaOuFH?J=SK(LZs`YsOJ9)I<#H6IeWX7GXb66Oc$au0ZdDy-w`jWInpUT2)PxntiXR4OC zMwFx^mZVxG7o`Fz1|tJQQ(XfyT?5MyLlY|#b1Oq5Z381K1B3n>)1RVf$jwj5OshoJ YU|^1{;hw{yhd>Psp00i_>zopr0MjCgO#lD@ literal 0 HcmV?d00001 diff --git a/Baker/icon-categories@2x.png b/Baker/icon-categories@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..998f88c78c0b82c2e34fb25e68294e20b933f7ad GIT binary patch literal 515 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-2>S4={E+nQaGTu$OrHy0YJA<7C(8IV^Z*Gf?Q8r;B4q#NoHocJnnEh_u-Y#K@E~ zm>L#-=)4nKqa$7(@7b%y)V#a%%4xOMfRzg;KCKa6TJWzpm#ayEqqpK#@qq~!8hww7 zhDzwp*pPZ~@zToq%G%#bnHU8(Hgn`v$$bd|VkrG={>g8a8=gH0Qb;)#6K$XK zUVg4`k*Mo22k*c%>#%LEd=gXVXvVH%aTJL9#MY#)Z{1_MO%@meswJ)wB`Jv|saDBF zsX&Us$iUE4*T78Iz%so9m?LYr=dkD@ PPy>UftDnm{r-UW|7xu9q literal 0 HcmV?d00001 diff --git a/Baker/icon-categories@3x.png b/Baker/icon-categories@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..2a1e88cfda571d7bdb1344a6216cd0cb42408ab7 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^F(Ayr1|%Q9zZ3za*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeujz(>34ygi=HlyAr-gY-rU&BWGLc#k$sVhiqNeEypP^C z)ZD&d#&Oc@W``SxLL6t4WpS=3RoG+M3&;9>Ff8`3} zgR?pB`Fu!gGLQEC^H2Ze`4_(XXWqXk4N`qer|Y_XuiXQm*Q;7rH^+BhUmtYs=+_*C zmf63Km|d;>!pI0z9Jk`v(dUxCRc_BL?)Tj1F8^TOtJAB~b0)n>JGZyz-XG<4=`f3g h_so0%G9Cm%b}&BLNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk>Iuax9V7|cmrQVr04ps?GE{7)Y{xF{Kx%u(~wch?`S5I@Ryq=P-SJO=tGOy~VQC>)J|ZZ+Ynx{^8A~rRk2>XZlXI zmtCJx{qfm>ZK2&_Yj2f3INQDL)b+>0w}P%mJ(7{j-Z?F6_6gx_dvC9{n@We`!>6&AExn#m#$EOILmK%;5TFob;+sc6EBoTkcu%FL%f)7Tc$me%-gE zU+!REq1TLUDut6Y*;j^~P*4~0URK@W@#bLeGMnt{>u&NCy>`@@ls$XumIRNrEl1{d zW%2P_I);Sa@vLfb7gy4DJKA-k^|+kkRY&FRk2lWIP3A3Ga#+0lMZuLv`=XQY{n5z& zbGWAU?OA#GfQRZ)KZG@8-ly%T%lCCqNCt+pYKdz^NlIc#s#S7PDv)9@GB7mNH89gP zunaLYu`)5TGB(jRFtRc*@LNS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-2>S4={E+nQaGTu$OrHy0YJA<7C$oSe0I~6R7F6r;B4q#NoHs&-;gji?Dy-FIlF{ z5+Uf?>i2Wwgrhqa=)I8p%3rX$bD{TthRYkw?mm*g;1YcJT>Syw&e=&@^i&jgNxb1u zo3TM^S;!2=T@TJN&-tDf$G+_DkJ{P4@7aE@IsbcJanbvy+EEL%x85}``t{{OzNqy# znK`_U)493o0w$P0h<>A`9r>+2b^fJ`DvHUcnDuilJ1V#B`Ep>2&+bRQv$l)9{`F&X z+P##lmC9dcZB6<=V{vWj>#nYAQ}@q^G+h1t{*wny^Zzc<-xK=xWx02kS?=QN3komS zec79{Vdamj`gTjZ>uxkv;2RDDd@-Tj1A`T0qA6C$@JSoHq>EcV9r*SVcnT<+^l zKjT_?Yx9E|?eDhFt{!R|Fg97v4JtS1f96ubj6Q<}^&}-TA0%frMUw_+}x znL;i2S-9dC=G-|6p`{q<^Q zuN%|6wtst`UO_QmvAUQh^kMk%6J9u7R1Zfn|uHiIs_wm9dGofsvJgfd@<8GZYQE`6-!c fmB<bP0l+XkK*4e~* literal 0 HcmV?d00001 diff --git a/Baker/icon-info@3x.png b/Baker/icon-info@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..84723219cd246fed7018b2733db67a1a97f4480e GIT binary patch literal 1375 zcmV-l1)%zgP)*l6vsbRV<42*8Yt1W8yh2p#9dhw7Cc-*qI**!-MV5)Wa(efrVEm8 zOfbc`X8|!mR;*arMN5l`n*xo>4{aOKl;GmdOR#$9%zbZW&hUCZ$!wUuIp_Q3zWe6a zoePFx7=~dOhQWY#0)ZR^Mu9=#9pHfb_if-QumwB>HnkI#DADlufRBMwz)|2sU_9`% z>%bbY3fuvH03K-zDv?QG4p;_WMlQ>)?MZEs+Kd2S0xQHfxvs3Zm?PTab20|ZQr&-Z z8NIt~yBx-}%}B`*)mQtuqS3p{bC=VQwmGpmPt}XiHn^@lbUD8Ei~u(ZFf_E>Ao2Y@ zWw-DQ@GbCe4{huKKT~z{?g771^}DxS{Q;_e`C;G~RX6f|;H@5FZUEE3?S$S$=aMt4 zo2%?T)eqXIK&y+`E%(`3*H$+<>^Kd@GvI_a6}Sm308`I?+ko=F7G0dKH zxrF9Dubps*OAV4<0QPAo{0jSAPNDg$|B$u`>y7*k%xEX;VaDYc(tBxZC3%Ff(c>0y zPAkaYzJn@zi#0%-Y%q58XFUF9+ZcD)a z1hI1)zF&yhxosw0#E|Amc1Sm<$!$1Ji`4C%O%$6E+i@@W=qQ9noxo#tI?8i>y7oNX458RCvP}&idVx^`urUVa`V+9%&0^O&u z$@xSKAg_^*N{xj4Xtss=>n>Qo^U`Vc(UuwsG5nNJe_Pv$j7VA~uY$ORe`J|u0;dpK z_(z_%VgyhnDZS@NHG>=hvR*TdRm*$!8Wfb}y@Ig)8 zPrzrH@>m5v@zjs|e31-4DAfO&NhH8=;G>$loEvq15$e63h1^nn6#o!Y0wm2|&m_a8 zEBtqD^$JLud%D7n$Vi4uU!Ngu0SZW(y`D*iZwd9%EvFxmGky|pLJYqn)SuCIAtRDj$*UkrSz^;%wTDPcY@w8}h^%dRlttvwq5bzleeA8t zB_e5*JZgk(1d6l~DD(=yOLe$Q9fQEd#G*C1h=1C3g@~JgI@o5;2%9;ZVB3{5Z&wD} zke_u!KG-8CyG!y%PQV_jdbP|oZM2&aVvA~$Mmp(d2KXkT0mrHT=KM*--K$i6?yJJz zY*5V{{t+>5E%sn+#s_0N*rVT>vBM1%Xk*8BWXBGuQNWI1%8VT{rhpxzr+^(4se~Pc zT7w<-`(Iwm!+ty1an?%Mf$a(mVXq`m!CtzdguPZqkv;R`q!K&zef?^%5)J=9`sU?# hhG7_nVHk!1`~!d1ivQ}ny@3D#002ovPDHLkV1jByi;(~T literal 0 HcmV?d00001 diff --git a/Baker/icon-refresh.png b/Baker/icon-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..4a19a47bf421c12f9f462718bafbf26d937b5719 GIT binary patch literal 586 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Skr zJ|C*`*WA6DIRESY^v&lUT@DR*(@zh$Cm`h%Exvc&;_^pvo2Q*Up|rE<7fZ? literal 0 HcmV?d00001 diff --git a/Baker/icon-refresh@2x.png b/Baker/icon-refresh@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..845282876c4680dbdf842ceb6a2cd1f2de02ed9b GIT binary patch literal 893 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-2>S4={E+nQaGTu$OrHy0YJA<7C$s*j(fK1E{IM)5S3);_%z)`?G@sMULB_slQkaO9w`yR)FRMTFy~(hbr7S-jVB@oBkbbTnsVxz6U; z7?3gBeUe$Fp1EP=$+Nr7l{B9Go_65xzW3kn72mUc&cY_~VE*w5Y{m`(@&SqMwrkeu zn2VLhGV80)P&#OF_y^-;-D{PHv-fSfe~hP~Q#|L@oD1GfB9^Nnwk`MSJ3i<5?I%)a zHXZYn{mD@*vZB1he4f?pJIOvL)D3^Sgv8cO3eD0@Qtf(jGI+_Aq^bi(g*=nqB$i55 zEctZV=FyrTv1+HaGR zgr00+zUe<{QXQY)MB#@sFHQ?L+M#yo+cZJ;%FU;*2Jc|!Ec`kDv$K}`1HSE+S4z)@ z-F?I6e#AZVQ=HcOM_S<;+roV}PTD?oKVP%LVTLI`luRX(TEiz5#crK{JMY0^{sVTm zgPNu82ex_7y(RyKP1J{BPs{(lgM85p61G|wIXNZEPR zd+uLzzl*sav_5QJd-Y00qEq~yiQLOCZah${^!JqW`-kOoLpJ~STxh*ddQ0DX>2Lq8 z&Ub8H`H4NHf$^XFAEs5x(aP(h81sP1QMJT1q9i4;B-JXpC>2OC7#SFv>Kd5o8d!!H znpl|_TNzqt8yHy`7$`YsTtv~3o1c=IR*9^^z#Oc>45C5#iCPCx1B0ilpUXO@geCwF C3U3Sm literal 0 HcmV?d00001 diff --git a/Baker/icon-refresh@3x.png b/Baker/icon-refresh@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..a03eee06ab1d2c2c0b47528e7823244006ba6bb0 GIT binary patch literal 1259 zcmV`8z%$@xx!ngoWvFQe za9P@zG#L-H0)GLofF8&85;zR}4*Zm|wl-kDv_Wam0Bi)#<8E+N`&_=}8oqujlG0Yb zW}yuq3j7JYAYG&TYR`bJKw|=(Z57ukq~RrCGtillxl!#oX2kbTV0Ix5Zvn0f82$ft z5t!^~cHs=)2)qlx?B4bUSm|JPVGOS?3zaswTHB48=4@tr8(#AHa7-G2gTVX*pX~xp z180Hjz&+qGW@G~V0E`5hfJwj{;Ft2>U07Yd&T?Q2(4K=Yna~Ja!fmv@1NH$6fVu?t ztOu3?hk%cc_kIxLe}bJ->VZ=Z!M+8y1EW*a&;;xO{&Ub#Si|=^1aXA)p9(9Pj(Hrf zS;IHP6yJ?m6X!t**oJxftSQ4=h`0261pF+ZZz*Q_stLpEfNL>C-v!1B>YPJz)4dFj z`*<6d4VV!g#ZzD*W{D@oJYb;U{ZnBmX5DO?vs;99Mm^LNYD#I3A$FaxMqjgoF}wkL ziuH)F#x%?h#&Fv*up8MQ$+XN4#_$Ukk=q5erD=8$hBsp$-Dy6qyO6foK`aXwTlx=R z9&+-gjRI%=qUa!cL@X50l*PynVoE7twk1>(*GaMzs}EV4EpE7N{yvnmo~+FlpTnCi z{bw_=wQmYBXZyZln_%g`ky9M?nBS6)9PahP=fl2+kFfOL$-w}C55Se2?&XE=l+s}7 ze=Z$hX!si5Xz70~9bjmroex(m_!|Dw(r=q`zB_ykw@n$tqyr2MU&EhT`kSN!3=Lny zZ(8~%N(UGkzJ}kn^v{+Ku+|7Mc@;EioMP($7De>`3LKXXG$!MKX%YQ*F^366X(c+l zwpUu91IH|}tdtfAVZUN?VG_~%4nJY`kOx7?FOlQ2%M!!i(gG_9eVc@hshEsoR1C|c zMOIRTjX>3?!~P9@gvq%FMY~0G5As{x`r@}3x?811_C--Pb5=4BcyH1C7#Jiiv>KbR zkCrEx^{zBiu@8_QV1=~MD2lQnzZrNKL+3fdFL4t5k(2qvE2i)o`>upw2=P!=KVUuf zjI8SioSw^p`e6JzWL5DzAg(7<}byC)%+wQ93TMD%c zI05`Y7+J}02t5-PRqVGvCc*Rullgmq*$H+F^Db`2x>b V!`$A>Cma9(002ovPDHLkV1fc^GN=Fm literal 0 HcmV?d00001 diff --git a/Baker/icon-subscribe.png b/Baker/icon-subscribe.png new file mode 100644 index 0000000000000000000000000000000000000000..f0a2064e5c5e85d70c5f065ab0b9fb7d5746143d GIT binary patch literal 562 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk^d%l8qA^!aUzAM{wCWpWyz%CZ@T??}!@XMyV#1b!&1Yx^+^n z&urvX`Cj&XUh(sJ#vk0LDke+MSnYS$MnLR~ScQ^Lb-+|s?o}`Svw{}67{C3e6fDxE zd9-deM`OXTrtRrn}n}yBY*}S(@0CnM9nW_a2xb z8Ovq3m4U&I7SoF;8glbfGSe!NH5izKG#ElPq)%oy Q18QLKboFyt=akR{0FBkd_5c6? literal 0 HcmV?d00001 diff --git a/Baker/icon-subscribe@2x.png b/Baker/icon-subscribe@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c605d225989198a95173e5824995581bfb5774 GIT binary patch literal 882 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-2>S4={E+nQaGTu$OrHy0YJA<7Bts(*O8Z0H`U|)5S3);_%z)`?G@sMULB_TD?+Q$JgZ4-t^#6ii6;CzDZpd>kk~^ zYh5I8dy?8LHMQ$cB|oj!|85trR{ida#q*l|&*v4N-}(LE&Ilc|oGiXF*Cto7t1>cP z*ZkfwSY}O2cbl&9`;BmDVW9U#AweY_UhRJzmMl#&6CVn!DeQ76ICab5L2Zh_uTu{; z&r>*=*!^I_k~On)CYoOQ5glK5DdXBPd-sK+`yB!j&fSt-GUL^cc}l6Ne-z8CABl66 zaYi!dF^8@ECJ+*Nbu07T|7zX|$6MLW9(M5PXO>8vowV@cWDeui2es3FP5%@* zKW6nhE&ua+|1J16{tDN0{_c@ZEuK>&L5e@;(4#A}6n(*oS)15@+l4Q0x%4~Lx@n135B3fp9CjX9A zeg(Tre_pwnICCT4TZz+ew*2}dzWoZP+{<&-IYRHU1=A+m-hH)DYfry9hgZ&IgBc&C zIJapkR;>LcGw+#pWLN?3mXeK5)12>({-B-CL;lfASwj z$=4em?0sRw4@`rqC9V-ADTyViR>?)FK#IZ0z|d6Jz)aV`GQ`lt%GAut$Uxh`$jZQA pM~mr26b-rgDVb@N$QlgHK^hF98qz1Tn*lX2c)I$ztaD0e0suYHV6gxI literal 0 HcmV?d00001 diff --git a/Baker/icon-subscribe@3x.png b/Baker/icon-subscribe@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..0f505ffb08f4153da23a383d8dc5f55b8db43487 GIT binary patch literal 1266 zcmVN#EM|?Ac#mWLM&*ppyJVBPtilP)zm~OZE+jYq#kC5-NbD6{h2qv-`oB^@MW|6 z-kbT&_f6*Q&ilOq!!QiPFbu<}ErVXQV?a89f;{DAB;p4zBLRDX>t1`MgSinnLEG>X z(!umQw*f8MhPV7@wV`eo%@={8j`u_UGou2w0CTi^chH>QW<=%?|2I46kaTeU$Z5J8 zep))Xeq)mEW(Q452iZ>?r?=t9rGxAjb^=%EZTJweSUCdxrhC`+bcP3;?tA z?%hGN)T0xrL{nCx811E`R_(|!; zXlEQ)A)etYzDzY}yJ)@~Ii>+$rW#UQREB^ia62%HyXiaX-#0t9HGwhW-!8ibTmTk; zdH)*z{tH<2uVLHOD$OYR!=l-tJG_a(u(5SU4p-TL3b@SL6LW#~30nmI?AW$|8@h6t zw;990JHQ04S2l6x-X!gc5>{|0VFCAF@(XYjchU+o1)L||g{)t79=Jc&nY;^_QP3!@ zngQ;NWp>~e;FN;KY13C!c%9k`e59a}TJ#}_l91k@*j#nLXcc%ph0&cH!F^|yZhUH8 z1)ffAcHmLqZv`f#(jxGPl-YrMfL|4uQMG1)yQR$zi~>I@Fr_Mehr&GcZNL`_%!#ef zSeuisiUHtV1t!JP+tlQ)(W|&wI%&g-Y8iM*+Qhs(Nwy?RZMp7g!N}HHf z=ZH7~CB-=KvjQ`F=_e|ffRf@?;A;h@cGEQVyHV1ULEvKr=C0QV^zl=04d4?6CU=^h zl+<_aucK?(4Nb9B#yRABcF?S}8C5f`z~uEBOKCvrA?4x3^<^RHbgLPjI3_bZaSu;9 z6E*_;gT8#mYd*GrQ&g6+@%s|jmz6ERgQ+S@X@JJzBzbIRxFgv(o@xx=ihHu`=2Q*K z$t7SHE;+MOQuXjD^qLJWjqkn`6{Rvf=5W#i-t=lIjZvJ>iSsl3OI)scV8VYEqwG(m zO^Qj`+iY{d!A;6P$i^$a<29``%oJU*~2qJuYop8Q$vBsAXKzr$jEu zu&;Z$OKiuaO)JBh4H|R?xK}zbAHzoReJdSUQ3hGNxcWTsEV|Nv8$PD_jyPM?G<{+p z?cw7+bYF~i`WVA7^N@5vMR*PP9ye217p8p7N2LQP!X&zekE}GXrLhgeFbu;m48t%C c!`PVr0F)LR54>b9s{jB107*qoM6N<$f -@protocol BKRCategoryFilterItemDelegate +@protocol BKRCategoryFilterButtonDelegate @required -- (void)categoryFilterItem:(id)categoryFilterItem clickedAction:(NSString *)action; +- (void)categoryFilterButton:(id)categoryFilterButton clickedAction:(NSString *)action; @end -@interface BKRCategoryFilterItem : UIBarButtonItem { - id delegate; +@interface BKRCategoryFilterButton : UIButton { + id delegate; } @property (retain) id delegate; @@ -46,7 +46,8 @@ @property (nonatomic, strong) NSArray *categoriesActionSheetActions; @property (nonatomic, strong) NSArray *categories; --(id)initWithCategories:(NSArray *)aCategories delegate:(NSObject *)aDelegate; --(id)initWithDelegate:(NSObject *)aDelegate; +-(id)initWithCoder:(NSCoder *)aDecoder; +-(void)setCategories:(NSArray *)aCategories delegate:(NSObject *)aDelegate; + @end \ No newline at end of file diff --git a/BakerComponents/BKRCategoryFilterItem.m b/BakerComponents/BKRCategoryFilterButton.m similarity index 79% rename from BakerComponents/BKRCategoryFilterItem.m rename to BakerComponents/BKRCategoryFilterButton.m index 426ae7c9..e4d6d3c1 100644 --- a/BakerComponents/BKRCategoryFilterItem.m +++ b/BakerComponents/BKRCategoryFilterButton.m @@ -30,38 +30,33 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#import "BKRCategoryFilterItem.h" +#import "BKRCategoryFilterButton.h" +#import "BKRShelfViewController.h" -@implementation BKRCategoryFilterItem { +@implementation BKRCategoryFilterButton { } @synthesize delegate; --(id)initWithCategories:(NSArray *)aCategories delegate:(NSObject *)aDelegate { - - // Initialize dropdown - if(self = [super initWithTitle:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil) style:UIBarButtonItemStylePlain target:self action:@selector(categoryFilterItemTouched:)]) { - - // Set categories - self.categories = aCategories; - - // Set delegate - self.delegate = aDelegate; - } +-(id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + [self setTitle:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil) forState:UIControlStateNormal]; + [self addTarget:self action:@selector(categoryFilterButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; return self; } --(id)initWithDelegate:(NSObject *)aDelegate { - return [self initWithCategories:[NSArray array] delegate:aDelegate]; +-(void)setCategories:(NSArray *)aCategories delegate:(NSObject *)aDelegate { + self.categories = aCategories; + self.delegate = aDelegate; } -- (IBAction)categoryFilterItemTouched:(UIBarButtonItem *)sender { +- (void)categoryFilterButtonTouched:(UIButton *)sender { if (self.categoriesActionSheet.visible) { [self.categoriesActionSheet dismissWithClickedButtonIndex:(self.categoriesActionSheet.numberOfButtons - 1) animated:YES]; } else { self.categoriesActionSheet = [self buildCategoriesActionSheet]; - [self.categoriesActionSheet showFromBarButtonItem:sender animated:YES]; + [self.categoriesActionSheet showFromRect:self.frame inView:self.superview animated:YES]; } } @@ -95,11 +90,11 @@ - (void) actionSheet:(UIActionSheet*)actionSheet clickedButtonAtIndex:(NSInteger if (actionSheet == self.categoriesActionSheet && buttonIndex > -1) { NSString *action = [self.categoriesActionSheetActions objectAtIndex:buttonIndex]; if ([action isEqualToString:@"reset-filter"]) { - [self setTitle:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil)]; + [self setTitle:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil) forState:UIControlStateNormal]; } else { - [self setTitle:action]; + [self setTitle:action forState:UIControlStateNormal]; } - [self.delegate categoryFilterItem:self clickedAction:action]; + [self.delegate categoryFilterButton:self clickedAction:action]; } } diff --git a/BakerShelf/BKRCustomNavigationController.h b/BakerShelf/BKRCustomNavigationController.h index 36d94581..95bcf57b 100644 --- a/BakerShelf/BKRCustomNavigationController.h +++ b/BakerShelf/BKRCustomNavigationController.h @@ -34,4 +34,6 @@ @interface BKRCustomNavigationController : UINavigationController +@property (nonatomic, copy) UIColor *originalTitleColor; + @end diff --git a/BakerShelf/BKRCustomNavigationController.m b/BakerShelf/BKRCustomNavigationController.m index a8c3f2e4..006349ce 100644 --- a/BakerShelf/BKRCustomNavigationController.m +++ b/BakerShelf/BKRCustomNavigationController.m @@ -43,4 +43,26 @@ - (BOOL)shouldAutorotate { return [self.topViewController shouldAutorotate]; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self updateTitleForOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; +} + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { + [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + [self updateTitleForOrientation:toInterfaceOrientation]; +} + +- (void)updateTitleForOrientation:(UIInterfaceOrientation)orientation { + if(!self.originalTitleColor) { + NSDictionary *titleTextAttributes = self.navigationBar.titleTextAttributes; + self.originalTitleColor = [titleTextAttributes objectForKey:@"TextColor"]; + } + if(UIInterfaceOrientationIsPortrait(orientation) && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor clearColor], NSForegroundColorAttributeName, nil]]; + }else{ + [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:self.originalTitleColor, NSForegroundColorAttributeName, nil]]; + } +} + @end \ No newline at end of file diff --git a/BakerShelf/BKRIssue.h b/BakerShelf/BKRIssue.h index 5c1da1a3..a3f5f6c9 100644 --- a/BakerShelf/BKRIssue.h +++ b/BakerShelf/BKRIssue.h @@ -37,18 +37,22 @@ #import "BKRBook.h" -typedef enum transientStates { - BakerIssueTransientStatusNone, - BakerIssueTransientStatusDownloading, - BakerIssueTransientStatusOpening, - BakerIssueTransientStatusPurchasing, - BakerIssueTransientStatusUnpriced -} BakerIssueTransientStatus; +typedef enum issueStates { + BakerIssueStatusNone, + BakerIssueStatusDownloading, + BakerIssueStatusOpening, + BakerIssueStatusPurchasing, + BakerIssueStatusUnpriced +} BakerIssueStatus; + +@protocol BKRIssueDelegate; @interface BKRIssue : NSObject { BKRPurchasesManager *purchasesManager; } +@property (nonatomic, weak) id delegate; + @property (nonatomic, copy) NSString *ID; @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *info; @@ -56,16 +60,14 @@ typedef enum transientStates { @property (nonatomic, copy) NSURL *url; @property (nonatomic, copy) NSString *path; @property (nonatomic, copy) NSArray *categories; - @property (nonatomic, copy) NSString *coverPath; @property (nonatomic, copy) NSURL *coverURL; - @property (nonatomic, copy) NSString *productID; @property (nonatomic, copy) NSString *price; @property (nonatomic, strong) BKRBook *bakerBook; -@property (nonatomic, assign) BakerIssueTransientStatus transientStatus; +@property (nonatomic, assign) BakerIssueStatus status; @property (nonatomic, copy) NSString *notificationDownloadStartedName; @property (nonatomic, copy) NSString *notificationDownloadProgressingName; @@ -73,6 +75,8 @@ typedef enum transientStates { @property (nonatomic, copy) NSString *notificationDownloadErrorName; @property (nonatomic, copy) NSString *notificationUnzipErrorName; +@property (nonatomic, assign) BOOL purchaseDelayed; + - (id)initWithBakerBook:(BKRBook*)bakerBook; - (void)getCoverWithCache:(bool)cache andBlock:(void(^)(UIImage *img))completionBlock; - (NSString*)getStatus; @@ -80,5 +84,18 @@ typedef enum transientStates { - (id)initWithIssueData:(NSDictionary*)issueData; - (void)download; - (void)downloadWithAsset:(NKAssetDownload*)asset; +- (void)dataChanged; + +@end + + +@protocol BKRIssueDelegate + +- (void)issue:(BKRIssue *)issue downloadStarted:(NSDictionary *)userInfo; +- (void)issue:(BKRIssue *)issue downloadProgressing:(NSDictionary *)userInfo; +- (void)issue:(BKRIssue *)issue downloadFinished:(NSDictionary *)userInfo; +- (void)issue:(BKRIssue *)issue downloadError:(NSDictionary *)userInfo; +- (void)issue:(BKRIssue *)issue unzipError:(NSDictionary *)userInfo; +- (void)issue:(BKRIssue *)issue dataChanged:(NSDictionary *)userInfo; @end diff --git a/BakerShelf/BKRIssue.m b/BakerShelf/BKRIssue.m index 35de6a9b..52f162d7 100755 --- a/BakerShelf/BKRIssue.m +++ b/BakerShelf/BKRIssue.m @@ -57,6 +57,7 @@ - (id)initWithBakerBook:(BKRBook*)book { _productID = @""; _price = nil; _bakerBook = book; + _purchaseDelayed = false; _coverPath = @""; if (book.cover == nil) { @@ -66,21 +67,12 @@ - (id)initWithBakerBook:(BKRBook*)book { _coverPath = [book.path stringByAppendingPathComponent:book.cover]; } - _transientStatus = BakerIssueTransientStatusNone; + _status = BakerIssueStatusNone; - [self setNotificationDownloadNames]; } return self; } -- (void)setNotificationDownloadNames { - self.notificationDownloadStartedName = [NSString stringWithFormat:@"notification_download_started_%@", self.ID]; - self.notificationDownloadProgressingName = [NSString stringWithFormat:@"notification_download_progressing_%@", self.ID]; - self.notificationDownloadFinishedName = [NSString stringWithFormat:@"notification_download_finished_%@", self.ID]; - self.notificationDownloadErrorName = [NSString stringWithFormat:@"notification_download_error_%@", self.ID]; - self.notificationUnzipErrorName = [NSString stringWithFormat:@"notification_unzip_error_%@", self.ID]; -} - #pragma mark - Newsstand - (id)initWithIssueData:(NSDictionary*)issueData { @@ -109,12 +101,8 @@ - (id)initWithIssueData:(NSDictionary*)issueData { } else { self.path = nil; } - self.bakerBook = nil; - - self.transientStatus = BakerIssueTransientStatusNone; - - [self setNotificationDownloadNames]; + self.status = BakerIssueStatusNone; } return self; } @@ -142,21 +130,26 @@ - (void)download { NKAssetDownload *assetDownload = [nkIssue addAssetWithRequest:req]; [self downloadWithAsset:assetDownload]; } else { - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationDownloadErrorName object:self userInfo:nil]; + if(self.delegate) { + [self.delegate issue:self downloadError:nil]; + } } } - (void)downloadWithAsset:(NKAssetDownload*)asset { [asset downloadWithDelegate:self]; - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationDownloadStartedName object:self userInfo:nil]; + if(self.delegate) { + [self.delegate issue:self downloadStarted:nil]; + } } #pragma mark - Newsstand download management - (void)connection:(NSURLConnection*)connection didWriteData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten expectedTotalBytes:(long long)expectedTotalBytes { - NSDictionary *userInfo = @{@"totalBytesWritten": @(totalBytesWritten), - @"expectedTotalBytes": @(expectedTotalBytes)}; - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationDownloadProgressingName object:self userInfo:userInfo]; + NSDictionary *userInfo = @{@"totalBytesWritten": @(totalBytesWritten), @"expectedTotalBytes": @(expectedTotalBytes)}; + if(self.delegate) { + [self.delegate issue:self downloadProgressing:userInfo]; + } } - (void)connectionDidFinishDownloading:(NSURLConnection*)connection destinationURL:(NSURL*)destinationURL { @@ -183,7 +176,9 @@ - (void)unpackAssetDownload:(NKAssetDownload*)newsstandAssetDownload toURL:(NSUR if (!unzipSuccessful) { NSLog(@"[BakerShelf] Newsstand - Unable to unzip file: %@. The file may not be a valid HPUB archive.", [destinationURL path]); dispatch_async(dispatch_get_main_queue(), ^(void) { - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationUnzipErrorName object:self userInfo:nil]; + if(self.delegate) { + [self.delegate issue:self unzipError:nil]; + } }); } @@ -197,7 +192,9 @@ - (void)unpackAssetDownload:(NKAssetDownload*)newsstandAssetDownload toURL:(NSUR if (unzipSuccessful) { // Notification and UI update have to be handled on the main thread dispatch_async(dispatch_get_main_queue(), ^(void) { - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationDownloadFinishedName object:self userInfo:nil]; + if(self.delegate) { + [self.delegate issue:self downloadFinished:nil]; + } }); } @@ -222,11 +219,11 @@ - (void)connectionDidResumeDownloading:(NSURLConnection*)connection totalBytesWr - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error { NSLog(@"Connection error when trying to download %@: %@", [connection currentRequest].URL, [error localizedDescription]); - [connection cancel]; - NSDictionary *userInfo = @{@"error": error}; - [[NSNotificationCenter defaultCenter] postNotificationName:self.notificationDownloadErrorName object:self userInfo:userInfo]; + if(self.delegate) { + [self.delegate issue:self downloadError:userInfo]; + } } - (void)getCoverWithCache:(bool)cache andBlock:(void(^)(UIImage *img))completionBlock { @@ -251,14 +248,14 @@ - (void)getCoverWithCache:(bool)cache andBlock:(void(^)(UIImage *img))completion - (NSString*)getStatus { if ([BKRSettings sharedSettings].isNewsstand) { - switch (self.transientStatus) { - case BakerIssueTransientStatusDownloading: + switch (self.status) { + case BakerIssueStatusDownloading: return @"downloading"; break; - case BakerIssueTransientStatusOpening: + case BakerIssueStatusOpening: return @"opening"; break; - case BakerIssueTransientStatusPurchasing: + case BakerIssueStatusPurchasing: return @"purchasing"; break; default: @@ -284,4 +281,10 @@ - (NSString*)getStatus { } } +- (void)dataChanged { + if(self.delegate) { + [self.delegate issue:self dataChanged:nil]; + } +} + @end diff --git a/BakerShelf/BKRIssueViewController.h b/BakerShelf/BKRIssueCell.h similarity index 53% rename from BakerShelf/BKRIssueViewController.h rename to BakerShelf/BKRIssueCell.h index 5d685f7b..ef12c3b4 100644 --- a/BakerShelf/BKRIssueViewController.h +++ b/BakerShelf/BKRIssueCell.h @@ -1,11 +1,12 @@ // -// IssueViewController.h +// BKRIssueCell.h // Baker // // ========================================================================================== // // Copyright (c) 2010-2013, Davide Casali, Marco Colombo, Alessandro Morandi // Copyright (c) 2014, Andrew Krowczyk, Cédric Mériau, Pieter Claerhout +// Copyright (c) 2015, Andrew Krowczyk, Cédric Mériau, Pieter Claerhout, Tobias Strebitzer // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -30,64 +31,41 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#import #import "BKRIssue.h" -#import "BKRPurchasesManager.h" -@interface BKRIssueViewController : UIViewController { - NSString *currentAction; - BOOL purchaseDelayed; - BKRPurchasesManager *purchasesManager; -} +@protocol BKRIssueCellDelegate; -@property (nonatomic, strong) BKRIssue *issue; -@property (nonatomic, strong) UIButton *actionButton; -@property (nonatomic, strong) UIButton *archiveButton; -@property (nonatomic, strong) UIProgressView *progressBar; -@property (nonatomic, strong) UIActivityIndicatorView *spinner; -@property (nonatomic, strong) UILabel *loadingLabel; - -@property (nonatomic, strong) UIButton *issueCover; -@property (nonatomic, strong) UILabel *titleLabel; -@property (nonatomic, strong) UILabel *infoLabel; +@interface BKRIssueCell : UICollectionViewCell -@property (nonatomic, copy) NSString *currentStatus; +@property (nonatomic, weak) IBOutlet id delegate; -#pragma mark - Structs -typedef struct { - int cellPadding; - int thumbWidth; - int thumbHeight; - int contentOffset; -} UI; +@property (nonatomic, strong) IBOutlet UIButton *actionButton; +@property (nonatomic, strong) IBOutlet UIButton *archiveButton; +@property (nonatomic, strong) IBOutlet UIProgressView *progressBar; +@property (nonatomic, strong) IBOutlet UIActivityIndicatorView *spinner; +@property (nonatomic, strong) IBOutlet UILabel *loadingLabel; -#pragma mark - Init -- (id)initWithBakerIssue:(BKRIssue*)bakerIssue; +@property (nonatomic, strong) IBOutlet UIButton *issueCover; +@property (nonatomic, strong) IBOutlet UILabel *titleLabel; +@property (nonatomic, strong) IBOutlet UILabel *infoLabel; +@property (nonatomic, strong) IBOutlet NSLayoutConstraint *issueCoverRatioConstraint; -#pragma mark - View Lifecycle -- (void)refresh; -- (void)refresh:(NSString*)status; -- (void)refresh:(NSString*)status cache:(BOOL)cache; -- (void)refreshWithCache:(BOOL)cache; -- (void)refreshContentWithCache:(bool)cache; -- (void)preferredContentSizeChanged:(NSNotification*)notification; +@property (nonatomic, strong) BKRIssue *issue; #pragma mark - Issue management -- (void)actionButtonPressed:(UIButton*)sender; -- (void)download; -- (void)setPrice:(NSString*)price; -- (void)buy; -- (void)read; - -#pragma mark - Newsstand archive management -- (void)archiveButtonPressed:(UIButton*)sender; +- (IBAction)actionButtonPressed:(id)sender; +- (IBAction)archiveButtonPressed:(id)sender; -#pragma mark - Helper methods -+ (UI)getIssueContentMeasures; -+ (int)getIssueCellHeight; -+ (CGSize)getIssueCellSizeForOrientation:(UIInterfaceOrientation)orientation; +#pragma mark - View management +- (void)updateView; @end -@interface alertView: UIAlertView -@end +@protocol BKRIssueCellDelegate + +- (void)issueCell:(BKRIssueCell *)cell requestsReadActionForIssue:(BKRIssue *)issue; +- (void)issueCell:(BKRIssueCell *)cell requestsPurchaseActionForIssue:(BKRIssue *)issue; +- (void)issueCell:(BKRIssueCell *)cell requestsDownloadActionForIssue:(BKRIssue *)issue; +- (void)issueCell:(BKRIssueCell *)cell requestsArchiveActionForIssue:(BKRIssue *)issue; + +@end \ No newline at end of file diff --git a/BakerShelf/BKRIssueCell.m b/BakerShelf/BKRIssueCell.m new file mode 100644 index 00000000..8f4a614c --- /dev/null +++ b/BakerShelf/BKRIssueCell.m @@ -0,0 +1,208 @@ +// +// BKRIssueCell.m +// Baker +// +// ========================================================================================== +// +// Copyright (c) 2010-2013, Davide Casali, Marco Colombo, Alessandro Morandi +// Copyright (c) 2014, Andrew Krowczyk, Cédric Mériau, Pieter Claerhout +// Copyright (c) 2015, Andrew Krowczyk, Cédric Mériau, Pieter Claerhout, Tobias Strebitzer +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// Neither the name of the Baker Framework nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written +// permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#import +#import "BKRIssueCell.h" +#import "BKRSettings.h" +#import "BKRPurchasesManager.h" +#import "BKRUtils.h" + +@implementation BKRIssueCell + +-(id)init { + if (self = [super init]) { + self.issueCover.layer.shadowOpacity = 0.5; + self.issueCover.layer.shadowOffset = CGSizeMake(0, 2); + self.issueCover.layer.shouldRasterize = YES; + self.issueCover.layer.rasterizationScale = [UIScreen mainScreen].scale; + } + return self; +} + +#pragma mark - Issue management + +- (IBAction)actionButtonPressed:(id)sender { + NSString *status = [self.issue getStatus]; + if ([status isEqualToString:@"remote"] || [status isEqualToString:@"purchased"]) { + [self.delegate issueCell:self requestsDownloadActionForIssue:self.issue]; + } else if ([status isEqualToString:@"downloaded"] || [status isEqualToString:@"bundled"]) { + [self.delegate issueCell:self requestsReadActionForIssue:self.issue]; + } else if ([status isEqualToString:@"purchasable"]) { + [self.delegate issueCell:self requestsPurchaseActionForIssue:self.issue]; + } +} + +- (IBAction)archiveButtonPressed:(id)sender { + UIAlertView *updateAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ARCHIVE_ALERT_TITLE", nil) + message:NSLocalizedString(@"ARCHIVE_ALERT_MESSAGE", nil) + delegate:self + cancelButtonTitle:NSLocalizedString(@"ARCHIVE_ALERT_BUTTON_CANCEL", nil) + otherButtonTitles:NSLocalizedString(@"ARCHIVE_ALERT_BUTTON_OK", nil), nil]; + [updateAlert show]; +} + + +- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + if (buttonIndex == 1) { + [self.delegate issueCell:self requestsArchiveActionForIssue:self.issue]; + } +} + +#pragma mark - View management + +- (void)updateView { + [self.titleLabel setText:self.issue.title]; + [self.infoLabel setText:self.issue.info]; + + // SETUP COVER IMAGE + [self.issue getCoverWithCache:YES andBlock:^(UIImage *image) { + [self.issueCover setBackgroundImage:image forState:UIControlStateNormal]; + }]; + + NSString *status = [self.issue getStatus]; + if ([status isEqualToString:@"remote"]) { + [self.actionButton setTitle:NSLocalizedString(@"FREE_TEXT", nil) forState:UIControlStateNormal]; + [self.spinner stopAnimating]; + self.actionButton.hidden = NO; + self.archiveButton.hidden = YES; + self.progressBar.hidden = YES; + self.loadingLabel.hidden = YES; + } else if ([status isEqualToString:@"connecting"]) { + [self.spinner startAnimating]; + self.actionButton.hidden = YES; + self.archiveButton.hidden = YES; + self.progressBar.progress = 0; + self.loadingLabel.text = NSLocalizedString(@"CONNECTING_TEXT", nil); + self.loadingLabel.hidden = NO; + self.progressBar.hidden = YES; + } else if ([status isEqualToString:@"downloading"]) { + [self.spinner startAnimating]; + self.actionButton.hidden = YES; + self.archiveButton.hidden = YES; + self.progressBar.progress = 0; + self.loadingLabel.text = NSLocalizedString(@"DOWNLOADING_TEXT", nil); + self.loadingLabel.hidden = NO; + self.progressBar.hidden = NO; + } else if ([status isEqualToString:@"downloaded"]) { + [self.actionButton setTitle:NSLocalizedString(@"ACTION_DOWNLOADED_TEXT", nil) forState:UIControlStateNormal]; + [self.spinner stopAnimating]; + self.actionButton.hidden = NO; + self.archiveButton.hidden = NO; + self.loadingLabel.hidden = YES; + self.progressBar.hidden = YES; + } else if ([status isEqualToString:@"bundled"]) { + [self.actionButton setTitle:NSLocalizedString(@"ACTION_DOWNLOADED_TEXT", nil) forState:UIControlStateNormal]; + [self.spinner stopAnimating]; + self.actionButton.hidden = NO; + self.archiveButton.hidden = YES; + self.loadingLabel.hidden = YES; + self.progressBar.hidden = YES; + } else if ([status isEqualToString:@"opening"]) { + [self.spinner startAnimating]; + self.actionButton.hidden = YES; + self.archiveButton.hidden = YES; + self.loadingLabel.text = NSLocalizedString(@"OPENING_TEXT", nil); + self.loadingLabel.hidden = NO; + self.progressBar.hidden = YES; + } else if ([status isEqualToString:@"purchasable"]) { + [self.actionButton setTitle:self.issue.price forState:UIControlStateNormal]; + [self.spinner stopAnimating]; + self.actionButton.hidden = NO; + self.archiveButton.hidden = YES; + self.progressBar.hidden = YES; + self.loadingLabel.hidden = YES; + } else if ([status isEqualToString:@"purchasing"]) { + [self.spinner startAnimating]; + self.loadingLabel.text = NSLocalizedString(@"BUYING_TEXT", nil); + self.actionButton.hidden = YES; + self.archiveButton.hidden = YES; + self.progressBar.hidden = YES; + self.loadingLabel.hidden = NO; + } else if ([status isEqualToString:@"purchased"]) { + [self.actionButton setTitle:NSLocalizedString(@"ACTION_REMOTE_TEXT", nil) forState:UIControlStateNormal]; + [self.spinner stopAnimating]; + self.actionButton.hidden = NO; + self.archiveButton.hidden = YES; + self.progressBar.hidden = YES; + self.loadingLabel.hidden = YES; + } else if ([status isEqualToString:@"unpriced"]) { + [self.spinner startAnimating]; + self.loadingLabel.text = NSLocalizedString(@"RETRIEVING_TEXT", nil); + self.actionButton.hidden = YES; + self.archiveButton.hidden = YES; + self.progressBar.hidden = YES; + self.loadingLabel.hidden = NO; + } + +} + +- (void)issue:(BKRIssue *)issue downloadStarted:(NSDictionary *)userInfo { + [self updateView]; +} + +- (void)issue:(BKRIssue *)issue downloadProgressing:(NSDictionary *)userInfo { + float bytesWritten = [(userInfo)[@"totalBytesWritten"] floatValue]; + float bytesExpected = [(userInfo)[@"expectedTotalBytes"] floatValue]; + if ([[self.issue getStatus] isEqualToString:@"connecting"]) { + self.issue.status = BakerIssueStatusDownloading; + [self updateView]; + } + [self.progressBar setProgress:(bytesWritten / bytesExpected) animated:YES]; +} + +- (void)issue:(BKRIssue *)issue downloadFinished:(NSDictionary *)userInfo { + self.issue.status = BakerIssueStatusNone; + [self updateView]; +} + +- (void)issue:(BKRIssue *)issue downloadError:(NSDictionary *)userInfo { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"DOWNLOAD_FAILED_TITLE", nil) + message:NSLocalizedString(@"DOWNLOAD_FAILED_MESSAGE", nil) + buttonTitle:NSLocalizedString(@"DOWNLOAD_FAILED_CLOSE", nil)]; + self.issue.status = BakerIssueStatusNone; + [self updateView]; +} + +- (void)issue:(BKRIssue *)issue unzipError:(NSDictionary *)userInfo { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"UNZIP_FAILED_TITLE", nil) + message:NSLocalizedString(@"UNZIP_FAILED_MESSAGE", nil) + buttonTitle:NSLocalizedString(@"UNZIP_FAILED_CLOSE", nil)]; + self.issue.status = BakerIssueStatusNone; + [self updateView]; +} + +- (void)issue:(BKRIssue *)issue dataChanged:(NSDictionary *)userInfo { + [self updateView]; +} + +@end diff --git a/BakerShelf/BKRIssueViewController.m b/BakerShelf/BKRIssueViewController.m deleted file mode 100644 index bb219302..00000000 --- a/BakerShelf/BKRIssueViewController.m +++ /dev/null @@ -1,649 +0,0 @@ -// -// IssueViewController.m -// Baker -// -// ========================================================================================== -// -// Copyright (c) 2010-2013, Davide Casali, Marco Colombo, Alessandro Morandi -// Copyright (c) 2014, Andrew Krowczyk, Cédric Mériau, Pieter Claerhout -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of -// conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// Neither the name of the Baker Framework nor the names of its contributors may be used to -// endorse or promote products derived from this software without specific prior written -// permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -#import - -#import "BKRSettings.h" -#import "BKRIssueViewController.h" -#import "BKRZipArchive.h" -#import "BKRPurchasesManager.h" - -#import "UIColor+BakerExtensions.h" -#import "UIScreen+BakerExtensions.h" -#import "BKRUtils.h" - -@implementation BKRIssueViewController - -#pragma mark - Init - -- (id)initWithBakerIssue:(BKRIssue*)bakerIssue { - self = [super init]; - if (self) { - _issue = bakerIssue; - _currentStatus = nil; - - purchaseDelayed = NO; - - if ([BKRSettings sharedSettings].isNewsstand) { - purchasesManager = [BKRPurchasesManager sharedInstance]; - [self addPurchaseObserver:@selector(handleIssueRestored:) name:@"notification_issue_restored"]; - - [self addIssueObserver:@selector(handleDownloadStarted:) name:self.issue.notificationDownloadStartedName]; - [self addIssueObserver:@selector(handleDownloadProgressing:) name:self.issue.notificationDownloadProgressingName]; - [self addIssueObserver:@selector(handleDownloadFinished:) name:self.issue.notificationDownloadFinishedName]; - [self addIssueObserver:@selector(handleDownloadError:) name:self.issue.notificationDownloadErrorName]; - [self addIssueObserver:@selector(handleUnzipError:) name:self.issue.notificationUnzipErrorName]; - } - } - return self; -} - -#pragma mark - View Lifecycle - -- (void)viewDidLoad { - [super viewDidLoad]; - - CGSize cellSize = [BKRIssueViewController getIssueCellSizeForOrientation:self.interfaceOrientation]; - - self.view.frame = CGRectMake(0, 0, cellSize.width, cellSize.height); - self.view.backgroundColor = [UIColor clearColor]; - self.view.tag = 42; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(preferredContentSizeChanged:) name:UIContentSizeCategoryDidChangeNotification object:nil]; - - UI ui = [BKRIssueViewController getIssueContentMeasures]; - - self.issueCover = [UIButton buttonWithType:UIButtonTypeCustom]; - self.issueCover.frame = CGRectMake(ui.cellPadding, ui.cellPadding, ui.thumbWidth, ui.thumbHeight); - - self.issueCover.backgroundColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesCoverBackgroundColor]; - self.issueCover.adjustsImageWhenHighlighted = NO; - self.issueCover.adjustsImageWhenDisabled = NO; - - self.issueCover.layer.shadowOpacity = 0.5; - self.issueCover.layer.shadowOffset = CGSizeMake(0, 2); - self.issueCover.layer.shouldRasterize = YES; - self.issueCover.layer.rasterizationScale = [UIScreen mainScreen].scale; - - [self.issueCover addTarget:self action:@selector(actionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - [self.view addSubview:self.issueCover]; - - // SETUP TITLE LABEL - self.titleLabel = [[UILabel alloc] init]; - self.titleLabel.textColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesTitleColor]; - self.titleLabel.backgroundColor = [UIColor clearColor]; - self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; - self.titleLabel.textAlignment = NSTextAlignmentLeft; - - [self.view addSubview:self.titleLabel]; - - // SETUP INFO LABEL - self.infoLabel = [[UILabel alloc] init]; - self.infoLabel.textColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesInfoColor]; - self.infoLabel.backgroundColor = [UIColor clearColor]; - self.infoLabel.lineBreakMode = NSLineBreakByTruncatingTail; - self.infoLabel.textAlignment = NSTextAlignmentLeft; - - [self.view addSubview:self.infoLabel]; - - // SETUP ACTION BUTTON - self.actionButton = [UIButton buttonWithType:UIButtonTypeCustom]; - self.actionButton.backgroundColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesActionBackgroundColor]; - - [self.actionButton setTitle:NSLocalizedString(@"ACTION_DOWNLOADED_TEXT", nil) forState:UIControlStateNormal]; - [self.actionButton setTitleColor:[UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesActionButtonColor] forState:UIControlStateNormal]; - [self.actionButton addTarget:self action:@selector(actionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - - [self.view addSubview:self.actionButton]; - - // SETUP ARCHIVE BUTTON - self.archiveButton = [UIButton buttonWithType:UIButtonTypeCustom]; - self.archiveButton.backgroundColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesArchiveBackgroundColor]; - - [self.archiveButton setTitle:NSLocalizedString(@"ARCHIVE_TEXT", nil) forState:UIControlStateNormal]; - [self.archiveButton setTitleColor:[UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesArchiveButtonColor] forState:UIControlStateNormal]; - - if ([BKRSettings sharedSettings].isNewsstand) { - [self.archiveButton addTarget:self action:@selector(archiveButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - [self.view addSubview:self.archiveButton]; - } - - // SETUP DOWN/LOADING SPINNER AND LABEL - self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - self.spinner.color = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesLoadingSpinnerColor]; - self.spinner.backgroundColor = [UIColor clearColor]; - self.spinner.hidesWhenStopped = YES; - - self.loadingLabel = [[UILabel alloc] init]; - self.loadingLabel.textColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesLoadingLabelColor]; - self.loadingLabel.backgroundColor = [UIColor clearColor]; - self.loadingLabel.textAlignment = NSTextAlignmentLeft; - self.loadingLabel.text = NSLocalizedString(@"DOWNLOADING_TEXT", nil); - - [self.view addSubview:self.spinner]; - [self.view addSubview:self.loadingLabel]; - - // SETUP PROGRESS BAR - self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; - self.progressBar.progressTintColor = [UIColor bkrColorWithHexString:[BKRSettings sharedSettings].issuesProgressbarTintColor]; - - [self.view addSubview:self.progressBar]; - - if ([BKRSettings sharedSettings].isNewsstand) { - // RESUME PENDING NEWSSTAND DOWNLOAD - NKLibrary *nkLib = [NKLibrary sharedLibrary]; - for (NKAssetDownload *asset in [nkLib downloadingAssets]) { - if ([asset.issue.name isEqualToString:self.issue.ID]) { - NSLog(@"[BakerShelf] Resuming abandoned Newsstand download: %@", asset.issue.name); - [self.issue downloadWithAsset:asset]; - } - } - } - - [self refreshContentWithCache:NO]; -} - -- (void)refreshContentWithCache:(bool)cache { - UIFont *titleFont = [UIFont fontWithName:[BKRSettings sharedSettings].issuesTitleFont - size:[BKRSettings sharedSettings].issuesTitleFontSize - ]; - UIFont *infoFont = [UIFont fontWithName:[BKRSettings sharedSettings].issuesInfoFont - size:[BKRSettings sharedSettings].issuesInfoFontSize - ]; - UIFont *actionFont = [UIFont fontWithName:[BKRSettings sharedSettings].issuesActionFont - size:[BKRSettings sharedSettings].issuesActionFontSize - ]; - UIFont *archiveFont = [UIFont fontWithName:[BKRSettings sharedSettings].issuesArchiveFont - size:[BKRSettings sharedSettings].issuesArchiveFontSize - ]; - - UI ui = [BKRIssueViewController getIssueContentMeasures]; - int heightOffset = ui.cellPadding; - uint textLineheight = [@"The brown fox jumps over the lazy dog" boundingRectWithSize:CGSizeMake(MAXFLOAT,MAXFLOAT) - options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine - attributes:@{NSFontAttributeName: infoFont} - context:nil].size.height; - - // SETUP COVER IMAGE - [self.issue getCoverWithCache:cache andBlock:^(UIImage *image) { - [self.issueCover setBackgroundImage:image forState:UIControlStateNormal]; - }]; - - CGFloat labelWidth = self.view.frame.size.width - ui.contentOffset - 60; - - // SETUP TITLE LABEL - self.titleLabel.font = titleFont; - self.titleLabel.frame = CGRectMake(ui.contentOffset, heightOffset, labelWidth, 60); - self.titleLabel.numberOfLines = 3; - self.titleLabel.text = self.issue.title; - [self.titleLabel sizeToFit]; - - heightOffset = heightOffset + self.titleLabel.frame.size.height + 5; - - // SETUP INFO LABEL - self.infoLabel.font = infoFont; - self.infoLabel.frame = CGRectMake(ui.contentOffset, heightOffset, labelWidth, 60); - self.infoLabel.numberOfLines = 3; - self.infoLabel.text = self.issue.info; - [self.infoLabel sizeToFit]; - -// heightOffset = heightOffset + self.infoLabel.frame.size.height + 5; - - heightOffset = 130 + textLineheight + 10; - - // SETUP ACTION BUTTON - NSString *status = [self.issue getStatus]; - if ([status isEqualToString:@"remote"] || [status isEqualToString:@"purchasable"] || [status isEqualToString:@"purchased"]) { - self.actionButton.frame = CGRectMake(ui.contentOffset, heightOffset, 110, 30); - } else if ([status isEqualToString:@"downloaded"] || [status isEqualToString:@"bundled"]) { - self.actionButton.frame = CGRectMake(ui.contentOffset, heightOffset, 80, 30); - } - self.actionButton.titleLabel.font = actionFont; - - // SETUP ARCHIVE BUTTON - self.archiveButton.frame = CGRectMake(ui.contentOffset + 80 + 10, heightOffset, 80, 30); - self.archiveButton.titleLabel.font = archiveFont; - - // SETUP DOWN/LOADING SPINNER AND LABEL - self.spinner.frame = CGRectMake(ui.contentOffset, heightOffset, 30, 30); - self.loadingLabel.frame = CGRectMake(ui.contentOffset + self.spinner.frame.size.width + 10, heightOffset, 135, 30); - self.loadingLabel.font = actionFont; - -// heightOffset = heightOffset + self.loadingLabel.frame.size.height + 5; - - // SETUP PROGRESS BAR - self.progressBar.frame = CGRectMake(ui.contentOffset, 136, labelWidth, 30); -} - -- (void)preferredContentSizeChanged:(NSNotification*)notification { - [self refreshContentWithCache:YES]; -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [self refresh]; -} - -- (void)refresh { - [self refresh:[self.issue getStatus]]; -} - -- (void)refresh:(NSString*)status { - [self refresh:[self.issue getStatus] cache:YES]; -} - -- (void)refreshWithCache:(BOOL)cache { - [self refresh:[self.issue getStatus] cache:cache]; -} - -- (void)refresh:(NSString*)status cache:(BOOL)cache { - // NSLog(@"[BakerShelf] Shelf UI - Refreshing %@ item with status from <%@> to <%@>", self.issue.ID, self.currentStatus, status); - if ([status isEqualToString:@"remote"]) { - [self.actionButton setTitle:NSLocalizedString(@"FREE_TEXT", nil) forState:UIControlStateNormal]; - [self.spinner stopAnimating]; - - self.actionButton.hidden = NO; - self.archiveButton.hidden = YES; - self.progressBar.hidden = YES; - self.loadingLabel.hidden = YES; - } else if ([status isEqualToString:@"connecting"]) { - NSLog(@"[BakerShelf] '%@' is Connecting...", self.issue.ID); - [self.spinner startAnimating]; - - self.actionButton.hidden = YES; - self.archiveButton.hidden = YES; - self.progressBar.progress = 0; - self.loadingLabel.text = NSLocalizedString(@"CONNECTING_TEXT", nil); - self.loadingLabel.hidden = NO; - self.progressBar.hidden = YES; - } else if ([status isEqualToString:@"downloading"]) { - NSLog(@"[BakerShelf] '%@' is Downloading...", self.issue.ID); - [self.spinner startAnimating]; - - self.actionButton.hidden = YES; - self.archiveButton.hidden = YES; - self.progressBar.progress = 0; - self.loadingLabel.text = NSLocalizedString(@"DOWNLOADING_TEXT", nil); - self.loadingLabel.hidden = NO; - self.progressBar.hidden = NO; - } else if ([status isEqualToString:@"downloaded"]) { - NSLog(@"[BakerShelf] '%@' is Ready to be Read.", self.issue.ID); - [self.actionButton setTitle:NSLocalizedString(@"ACTION_DOWNLOADED_TEXT", nil) forState:UIControlStateNormal]; - [self.spinner stopAnimating]; - - self.actionButton.hidden = NO; - self.archiveButton.hidden = NO; - self.loadingLabel.hidden = YES; - self.progressBar.hidden = YES; - } else if ([status isEqualToString:@"bundled"]) { - [self.actionButton setTitle:NSLocalizedString(@"ACTION_DOWNLOADED_TEXT", nil) forState:UIControlStateNormal]; - [self.spinner stopAnimating]; - - self.actionButton.hidden = NO; - self.archiveButton.hidden = YES; - self.loadingLabel.hidden = YES; - self.progressBar.hidden = YES; - } else if ([status isEqualToString:@"opening"]) { - [self.spinner startAnimating]; - - self.actionButton.hidden = YES; - self.archiveButton.hidden = YES; - self.loadingLabel.text = NSLocalizedString(@"OPENING_TEXT", nil); - self.loadingLabel.hidden = NO; - self.progressBar.hidden = YES; - } else if ([status isEqualToString:@"purchasable"]) { - [self.actionButton setTitle:self.issue.price forState:UIControlStateNormal]; - [self.spinner stopAnimating]; - - self.actionButton.hidden = NO; - self.archiveButton.hidden = YES; - self.progressBar.hidden = YES; - self.loadingLabel.hidden = YES; - } else if ([status isEqualToString:@"purchasing"]) { - NSLog(@"[BakerShelf] '%@' is being Purchased...", self.issue.ID); - [self.spinner startAnimating]; - - self.loadingLabel.text = NSLocalizedString(@"BUYING_TEXT", nil); - - self.actionButton.hidden = YES; - self.archiveButton.hidden = YES; - self.progressBar.hidden = YES; - self.loadingLabel.hidden = NO; - } else if ([status isEqualToString:@"purchased"]) { - NSLog(@"[BakerShelf] '%@' is Purchased.", self.issue.ID); - - [self.actionButton setTitle:NSLocalizedString(@"ACTION_REMOTE_TEXT", nil) forState:UIControlStateNormal]; - [self.spinner stopAnimating]; - - self.actionButton.hidden = NO; - self.archiveButton.hidden = YES; - self.progressBar.hidden = YES; - self.loadingLabel.hidden = YES; - } else if ([status isEqualToString:@"unpriced"]) { - [self.spinner startAnimating]; - - self.loadingLabel.text = NSLocalizedString(@"RETRIEVING_TEXT", nil); - - self.actionButton.hidden = YES; - self.archiveButton.hidden = YES; - self.progressBar.hidden = YES; - self.loadingLabel.hidden = NO; - } - - [self refreshContentWithCache:cache]; - - self.currentStatus = status; -} - -#pragma mark - Issue management - -- (void)actionButtonPressed:(UIButton*)sender { - NSString *status = [self.issue getStatus]; - if ([status isEqualToString:@"remote"] || [status isEqualToString:@"purchased"]) { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueDownload" object:self]; // -> Baker Analytics Event - [self download]; - } - } else if ([status isEqualToString:@"downloaded"] || [status isEqualToString:@"bundled"]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueOpen" object:self]; // -> Baker Analytics Event - [self read]; - } else if ([status isEqualToString:@"downloading"]) { - // TODO: assuming it is supported by NewsstandKit, implement a "Cancel" operation - } else if ([status isEqualToString:@"purchasable"]) { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssuePurchase" object:self]; // -> Baker Analytics Event - [self buy]; - } - } -} - -- (void)download { - [self.issue download]; -} - -- (void)buy { - [self addPurchaseObserver:@selector(handleIssuePurchased:) name:@"notification_issue_purchased"]; - [self addPurchaseObserver:@selector(handleIssuePurchaseFailed:) name:@"notification_issue_purchase_failed"]; - - if (![purchasesManager purchase:self.issue.productID]) { - // Still retrieving SKProduct: delay purchase - purchaseDelayed = YES; - - [self removePurchaseObserver:@"notification_issue_purchased"]; - [self removePurchaseObserver:@"notification_issue_purchase_failed"]; - - [purchasesManager retrievePriceFor:self.issue.productID]; - - self.issue.transientStatus = BakerIssueTransientStatusUnpriced; - [self refresh]; - } else { - self.issue.transientStatus = BakerIssueTransientStatusPurchasing; - [self refresh]; - } -} - -- (void)handleIssuePurchased:(NSNotification*)notification { - SKPaymentTransaction *transaction = notification.userInfo[@"transaction"]; - - if ([transaction.payment.productIdentifier isEqualToString:self.issue.productID]) { - - [self removePurchaseObserver:@"notification_issue_purchased"]; - [self removePurchaseObserver:@"notification_issue_purchase_failed"]; - - [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; - - if ([purchasesManager finishTransaction:transaction]) { - if (!transaction.originalTransaction) { - // Do not show alert on restoring a transaction - [BKRUtils showAlertWithTitle:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_TITLE", nil) - message:[NSString stringWithFormat:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_MESSAGE", nil), self.issue.title] - buttonTitle:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_CLOSE", nil)]; - } - } else { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_TITLE", nil) - message:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_MESSAGE", nil) - buttonTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_CLOSE", nil)]; - } - - self.issue.transientStatus = BakerIssueTransientStatusNone; - - [purchasesManager retrievePurchasesFor:[NSSet setWithObject:self.issue.productID] withCallback:^(NSDictionary *purchases) { - [self refresh]; - }]; - } -} - -- (void)handleIssuePurchaseFailed:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - - if ([transaction.payment.productIdentifier isEqualToString:self.issue.productID]) { - // Show an error, unless it was the user who cancelled the transaction - if (transaction.error.code != SKErrorPaymentCancelled) { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"ISSUE_PURCHASE_FAILED_TITLE", nil) - message:[transaction.error localizedDescription] - buttonTitle:NSLocalizedString(@"ISSUE_PURCHASE_FAILED_CLOSE", nil)]; - } - - [self removePurchaseObserver:@"notification_issue_purchased"]; - [self removePurchaseObserver:@"notification_issue_purchase_failed"]; - - self.issue.transientStatus = BakerIssueTransientStatusNone; - [self refresh]; - } -} - -- (void)handleIssueRestored:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - - if ([transaction.payment.productIdentifier isEqualToString:self.issue.productID]) { - [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; - - if (![purchasesManager finishTransaction:transaction]) { - NSLog(@"[BakerShelf] Could not confirm purchase restore with remote server for %@", transaction.payment.productIdentifier); - } - - self.issue.transientStatus = BakerIssueTransientStatusNone; - [self refresh]; - } -} - -- (void)setPrice:(NSString*)price { - self.issue.price = price; - if (purchaseDelayed) { - purchaseDelayed = NO; - [self buy]; - } else { - [self refresh]; - } -} - -- (void)read { - self.issue.transientStatus = BakerIssueTransientStatusOpening; - [self refresh]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"read_issue_request" object:self]; -} - -#pragma mark - Newsstand download management - -- (void)handleDownloadStarted:(NSNotification*)notification { - [self refresh]; -} - -- (void)handleDownloadProgressing:(NSNotification*)notification { - float bytesWritten = [(notification.userInfo)[@"totalBytesWritten"] floatValue]; - float bytesExpected = [(notification.userInfo)[@"expectedTotalBytes"] floatValue]; - - if ([self.currentStatus isEqualToString:@"connecting"]) { - self.issue.transientStatus = BakerIssueTransientStatusDownloading; - [self refresh]; - } - [self.progressBar setProgress:(bytesWritten / bytesExpected) animated:YES]; -} - -- (void)handleDownloadFinished:(NSNotification*)notification { - self.issue.transientStatus = BakerIssueTransientStatusNone; - [self refresh]; -} - -- (void)handleDownloadError:(NSNotification*)notification { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"DOWNLOAD_FAILED_TITLE", nil) - message:NSLocalizedString(@"DOWNLOAD_FAILED_MESSAGE", nil) - buttonTitle:NSLocalizedString(@"DOWNLOAD_FAILED_CLOSE", nil)]; - - self.issue.transientStatus = BakerIssueTransientStatusNone; - [self refresh]; -} - -- (void)handleUnzipError:(NSNotification*)notification { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"UNZIP_FAILED_TITLE", nil) - message:NSLocalizedString(@"UNZIP_FAILED_MESSAGE", nil) - buttonTitle:NSLocalizedString(@"UNZIP_FAILED_CLOSE", nil)]; - - self.issue.transientStatus = BakerIssueTransientStatusNone; - [self refresh]; -} - -#pragma mark - Newsstand archive management - -- (void)archiveButtonPressed:(UIButton*)sender { - UIAlertView *updateAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ARCHIVE_ALERT_TITLE", nil) - message:NSLocalizedString(@"ARCHIVE_ALERT_MESSAGE", nil) - delegate:self - cancelButtonTitle:NSLocalizedString(@"ARCHIVE_ALERT_BUTTON_CANCEL", nil) - otherButtonTitles:NSLocalizedString(@"ARCHIVE_ALERT_BUTTON_OK", nil), nil - ]; - [updateAlert show]; -} - -- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - if (buttonIndex == 1){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueArchive" object:self]; // -> Baker Analytics Event - - NKLibrary *nkLib = [NKLibrary sharedLibrary]; - NKIssue *nkIssue = [nkLib issueWithName:self.issue.ID]; - NSString *name = nkIssue.name; - NSDate *date = nkIssue.date; - - [nkLib removeIssue:nkIssue]; - - nkIssue = [nkLib addIssueWithName:name date:date]; - self.issue.path = [[nkIssue contentURL] path]; - - [self refresh]; - } -} - -#pragma mark - Helper methods - -- (void)addPurchaseObserver:(SEL)notificationSelector name:(NSString*)notificationName { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:notificationSelector - name:notificationName - object:purchasesManager]; - } -} - -- (void)removePurchaseObserver:(NSString*)notificationName { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] removeObserver:self - name:notificationName - object:purchasesManager]; - } -} - -- (void)addIssueObserver:(SEL)notificationSelector name:(NSString*)notificationName { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:notificationSelector - name:notificationName - object:nil]; - } -} - -+ (UI)getIssueContentMeasures { - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - UI iPad = { - .cellPadding = 30, - .thumbWidth = 135, - .thumbHeight = 180, - .contentOffset = 184 - }; - return iPad; - } else { - UI iPhone = { - .cellPadding = 22, - .thumbWidth = 87, - .thumbHeight = 116, - .contentOffset = 128 - }; - return iPhone; - } -} - -+ (int)getIssueCellHeight { - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - return 240; - } else { - return 190; - } -} - -+ (CGSize)getIssueCellSizeForOrientation:(UIInterfaceOrientation)orientation { - - CGFloat screenWidth = [[UIScreen mainScreen] bkrWidthForOrientation:orientation]; - int cellHeight = [BKRIssueViewController getIssueCellHeight]; - - if (screenWidth > 700) { - return CGSizeMake(screenWidth/2, cellHeight); - } else { - return CGSizeMake(screenWidth, cellHeight); - } - - /* - CGRect screenRect = [UIScreen mainScreen].bounds; - CGFloat screenWidth = screenRect.size.width; - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - return CGSizeMake(screenWidth / 2, [IssueViewController getIssueCellHeight]); - //return CGSizeMake((MIN(screenRect.size.width, screenRect.size.height) - 10) / 2, [IssueViewController getIssueCellHeight]); - } else { - if (screenWidth > 700) { - return CGSizeMake((screenWidth - 10) / 2, [IssueViewController getIssueCellHeight]); - } else { - return CGSizeMake(MIN(screenRect.size.width, screenRect.size.height) - 10, [IssueViewController getIssueCellHeight]); - } - } - */ - -} - -@end diff --git a/BakerShelf/BKRIssuesManager.h b/BakerShelf/BKRIssuesManager.h index 1fcf8c60..d28c4d25 100644 --- a/BakerShelf/BKRIssuesManager.h +++ b/BakerShelf/BKRIssuesManager.h @@ -48,6 +48,8 @@ - (NSSet*)productIDs; - (BOOL)hasProductIDs; - (BKRIssue*)latestIssue; +- (BKRIssue*)issueWithId:(NSString *)issueId; +- (BKRIssue*)issueWithProductId:(NSString *)productId; + (NSArray*)localBooksList; @end diff --git a/BakerShelf/BKRIssuesManager.m b/BakerShelf/BKRIssuesManager.m index bdcc2faa..95b32610 100644 --- a/BakerShelf/BKRIssuesManager.m +++ b/BakerShelf/BKRIssuesManager.m @@ -218,6 +218,24 @@ - (BKRIssue*)latestIssue { return self.issues[0]; } +- (BKRIssue*)issueWithProductId:(NSString *)productId { + for (BKRIssue *issue in self.issues) { + if ([issue.productID isEqualToString:productId]) { + return issue; + } + } + return nil; +} + +- (BKRIssue*)issueWithId:(NSString *)issueId { + for (BKRIssue *issue in self.issues) { + if ([issue.ID isEqualToString:issueId]) { + return issue; + } + } + return nil; +} + + (NSArray*)localBooksList { NSMutableArray *booksList = [NSMutableArray array]; NSFileManager *localFileManager = [NSFileManager defaultManager]; diff --git a/BakerShelf/BKRPurchasesManager.h b/BakerShelf/BKRPurchasesManager.h index a979735a..c7628692 100644 --- a/BakerShelf/BKRPurchasesManager.h +++ b/BakerShelf/BKRPurchasesManager.h @@ -33,11 +33,14 @@ #import #import +@protocol BKRPurchasesManagerDelegate; + @interface BKRPurchasesManager : NSObject { NSMutableDictionary *_purchases; BOOL _enableProductRequestFailureNotifications; } +@property (nonatomic, weak) IBOutlet id delegate; @property (nonatomic, strong) NSMutableDictionary *products; @property (nonatomic, strong) NSNumberFormatter *numberFormatter; @property (nonatomic, assign) BOOL subscribed; @@ -79,3 +82,19 @@ - (BOOL)hasSubscriptions; @end + +@protocol BKRPurchasesManagerDelegate + +- (void)purchasesManager:(BKRPurchasesManager *)manager retrievedProductIds:(NSMutableSet *)productIds; +- (void)purchasesManager:(BKRPurchasesManager *)manager productsRequestFailedWithError:(NSError *)error; +- (void)purchasesManager:(BKRPurchasesManager *)manager purchasedSubscriptionWithTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager subscriptionFailedForTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager subscriptionRestoredForTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager productRestoredForTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager restoreFailedWithError:(NSError *)error; +- (void)purchasesManager:(BKRPurchasesManager *)manager handleMultipleRestores:(NSArray *)transactions; +- (void)purchasesManager:(BKRPurchasesManager *)manager handleNotRecognizedTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager purchasedProductWithTransaction:(SKPaymentTransaction *)transaction; +- (void)purchasesManager:(BKRPurchasesManager *)manager productPurchaseFailedForTransaction:(SKPaymentTransaction *)transaction; + +@end diff --git a/BakerShelf/BKRPurchasesManager.m b/BakerShelf/BKRPurchasesManager.m index ff0b9151..99116109 100644 --- a/BakerShelf/BKRPurchasesManager.m +++ b/BakerShelf/BKRPurchasesManager.m @@ -114,15 +114,13 @@ - (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProduc NSLog(@"Invalid product identifier: %@", productID); } - NSMutableSet *ids = [NSMutableSet setWithCapacity:response.products.count]; + NSMutableSet *productIds = [NSMutableSet setWithCapacity:response.products.count]; for (SKProduct *skProduct in response.products) { (self.products)[skProduct.productIdentifier] = skProduct; - [ids addObject:skProduct.productIdentifier]; + [productIds addObject:skProduct.productIdentifier]; } - NSDictionary *userInfo = @{@"ids": ids}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_products_retrieved" object:self userInfo:userInfo]; - + [self.delegate purchasesManager:self retrievedProductIds:productIds]; } - (void)logProducts:(NSArray*)skProducts { @@ -134,16 +132,11 @@ - (void)logProducts:(NSArray*)skProducts { - (void)request:(SKRequest*)request didFailWithError:(NSError*)error { NSLog(@"App Store request failure: %@", error); - if (_enableProductRequestFailureNotifications) { - NSDictionary *userInfo = @{@"error": error}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_products_request_failed" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self productsRequestFailedWithError:error]; } - - // @TODO: REMOVE CODE - NSDictionary *userInfo = @{@"ids": @[]}; - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_products_retrieved" object:self userInfo:userInfo]; - + NSMutableSet *productIds = [NSMutableSet set]; + [self.delegate purchasesManager:self retrievedProductIds:productIds]; } - (NSString*)priceFor:(NSString*)productID { @@ -292,7 +285,7 @@ - (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transa } if (isRestoring) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_multiple_restores" object:self userInfo:nil]; + [self.delegate purchasesManager:self handleMultipleRestores:transactions]; } } @@ -320,42 +313,38 @@ - (void)logTransactions:(NSArray*)transactions { } - (void)completeTransaction:(SKPaymentTransaction*)transaction { - NSDictionary *userInfo = @{@"transaction": transaction}; NSString *productId = transaction.payment.productIdentifier; if ([productId isEqualToString:[BKRSettings sharedSettings].freeSubscriptionProductId] || [[BKRSettings sharedSettings].autoRenewableSubscriptionProductIds containsObject:productId]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_subscription_purchased" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self purchasedSubscriptionWithTransaction:transaction]; } else if ([self productFor:productId]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_issue_purchased" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self purchasedProductWithTransaction:transaction]; } else { NSLog(@"ERROR: Completed transaction for %@, which is not a Product ID this app recognises", productId); } } - (void)restoreTransaction:(SKPaymentTransaction*)transaction { - NSDictionary *userInfo = @{@"transaction": transaction}; NSString *productId = transaction.payment.productIdentifier; if ([productId isEqualToString:[BKRSettings sharedSettings].freeSubscriptionProductId] || [[BKRSettings sharedSettings].autoRenewableSubscriptionProductIds containsObject:productId]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_subscription_restored" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self subscriptionRestoredForTransaction:transaction]; } else if ([self productFor:productId]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_issue_restored" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self productRestoredForTransaction:transaction]; } else { NSLog(@"ERROR: Trying to restore %@, which is not a Product ID this app recognises", productId); - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_restored_issue_not_recognised" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self handleNotRecognizedTransaction:transaction]; } } - (void)failedTransaction:(SKPaymentTransaction*)transaction { NSLog(@"Payment transaction failure: %@", transaction.error); - - NSDictionary *userInfo = @{@"transaction": transaction}; NSString *productId = transaction.payment.productIdentifier; - + if ([productId isEqualToString:[BKRSettings sharedSettings].freeSubscriptionProductId] || [[BKRSettings sharedSettings].autoRenewableSubscriptionProductIds containsObject:productId]) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_subscription_failed" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self subscriptionFailedForTransaction:transaction]; } else { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_issue_purchase_failed" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self productPurchaseFailedForTransaction:transaction]; } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; @@ -366,15 +355,12 @@ - (void)restore { } - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue*)queue { - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_restore_finished" object:self userInfo:nil]; + // @TODO: Handle restore finished event (UI alert) } - (void)paymentQueue:(SKPaymentQueue*)queue restoreCompletedTransactionsFailedWithError:(NSError*)error { NSLog(@"Transaction restore failure: %@", error); - - NSDictionary *userInfo = @{@"error": error}; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"notification_restore_failed" object:self userInfo:userInfo]; + [self.delegate purchasesManager:self restoreFailedWithError:error]; } #pragma mark - Products diff --git a/BakerShelf/BKRShelfViewController.h b/BakerShelf/BKRShelfViewController.h index a94a12bc..27764560 100644 --- a/BakerShelf/BKRShelfViewController.h +++ b/BakerShelf/BKRShelfViewController.h @@ -38,60 +38,61 @@ #import "BKRShelfStatus.h" #import "BKRBakerAPI.h" #import "BKRPurchasesManager.h" -#import "BKRCategoryFilterItem.h" +#import "BKRCategoryFilterButton.h" +#import "BKRShelfViewLayout.h" +#import "BKRIssueCell.h" @class BKRShelfHeaderView; -@interface BKRShelfViewController : UIViewController { +@interface BKRShelfViewController : UIViewController { BKRBakerAPI *api; BKRIssuesManager *issuesManager; NSMutableArray *notRecognisedTransactions; UIPopoverController *infoPopover; BKRPurchasesManager *purchasesManager; - UIInterfaceOrientation realInterfaceOrientation; } @property (nonatomic, copy) NSArray *issues; @property (nonatomic, copy) NSArray *supportedOrientation; -@property (nonatomic, strong) NSMutableArray *issueViewControllers; @property (nonatomic, strong) BKRShelfStatus *shelfStatus; -@property (nonatomic, strong) UICollectionView *gridView; +@property (nonatomic, strong) IBOutlet UICollectionView *gridView; +@property (nonatomic, strong) IBOutlet BKRShelfViewLayout *layout; @property (nonatomic, strong) CAGradientLayer *gradientLayer; -@property (nonatomic, strong) BKRShelfHeaderView *headerView; -@property (nonatomic, strong) UIBarButtonItem *refreshButton; -@property (nonatomic, strong) UIBarButtonItem *subscribeButton; -@property (strong, nonatomic) UIBarButtonItem *infoItem; -@property (strong, nonatomic) BKRCategoryFilterItem *categoryItem; +@property (nonatomic, strong) IBOutlet BKRShelfHeaderView *headerView; +@property (nonatomic, strong) IBOutlet UIButton *refreshButton; +@property (nonatomic, strong) IBOutlet UIButton *subscribeButton; +@property (strong, nonatomic) IBOutlet UIButton *infoButton; +@property (strong, nonatomic) IBOutlet BKRCategoryFilterButton *categoryButton; @property (nonatomic, strong) UIActionSheet *subscriptionsActionSheet; @property (nonatomic, strong) NSArray *subscriptionsActionSheetActions; @property (nonatomic, strong) UIAlertView *blockingProgressView; @property (nonatomic, copy) NSString *bookToBeProcessed; +@property (nonatomic, strong) BKRIssue *issueToBeRead; + +- (IBAction)handleInfoButtonPressed:(id)sender; #pragma mark - Init -- (id)init; -- (id)initWithBooks:(NSArray*)currentBooks; +- (id)initWithCoder:(NSCoder *)aDecoder; +- (void)configureForNewsstand; +- (void)configureWithBooks:(NSArray*)currentBooks; #pragma mark - Shelf data source -- (void)handleRefresh:(NSNotification*)notification; - -#pragma mark - Store Kit -- (void)handleSubscription:(NSNotification*)notification; +- (IBAction)handleRefresh:(id)sender; #pragma mark - Navigation management - (void)readIssue:(BKRIssue*)issue; -- (void)handleReadIssue:(NSNotification*)notification; - (void)receiveBookProtocolNotification:(NSNotification*)notification; - (void)handleBookToBeProcessed; -- (void)pushViewControllerWithBook:(BKRBook*)book; +- (void)pushViewControllerWithIssue:(BKRIssue*)issue; #pragma mark - Buttons management -- (void)setrefreshButtonEnabled:(BOOL)enabled; +- (void)setRefreshButtonEnabled:(BOOL)enabled; - (void)setSubscribeButtonEnabled:(BOOL)enabled; -- (void)handleSubscribeButtonPressed:(NSNotification *)notification; +- (IBAction)handleSubscribeButtonPressed:(id)sender; #pragma mark - Helper methods - (int)getBannerHeight; diff --git a/BakerShelf/BKRShelfViewController.m b/BakerShelf/BKRShelfViewController.m index 5a6fb28d..53b13ed4 100644 --- a/BakerShelf/BKRShelfViewController.m +++ b/BakerShelf/BKRShelfViewController.m @@ -34,86 +34,56 @@ #import "BKRCustomNavigationBar.h" #import "BKRBookViewController.h" -#import "BKRIssueViewController.h" +#import "BKRIssueCell.h" #import "BKRShelfHeaderView.h" -#import "BKRShelfViewLayout.h" #import "BKRSettings.h" #import "NSData+BakerExtensions.h" #import "NSString+BakerExtensions.h" +#import "UIScreen+BakerExtensions.h" #import "BKRUtils.h" #import "MBProgressHUD.h" @interface BKRShelfViewController () -@property (nonatomic, strong) UICollectionViewFlowLayout *layout; - @end @implementation BKRShelfViewController #pragma mark - Init -- (id)init { - self = [super init]; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; if (self) { - if ([BKRSettings sharedSettings].isNewsstand) { - purchasesManager = [BKRPurchasesManager sharedInstance]; - [self addPurchaseObserver:@selector(handleProductsRetrieved:) - name:@"notification_products_retrieved"]; - [self addPurchaseObserver:@selector(handleProductsRequestFailed:) - name:@"notification_products_request_failed"]; - [self addPurchaseObserver:@selector(handleSubscriptionPurchased:) - name:@"notification_subscription_purchased"]; - [self addPurchaseObserver:@selector(handleSubscriptionFailed:) - name:@"notification_subscription_failed"]; - [self addPurchaseObserver:@selector(handleSubscriptionRestored:) - name:@"notification_subscription_restored"]; - [self addPurchaseObserver:@selector(handleRestoreFailed:) - name:@"notification_restore_failed"]; - [self addPurchaseObserver:@selector(handleMultipleRestores:) - name:@"notification_multiple_restores"]; - [self addPurchaseObserver:@selector(handleRestoredIssueNotRecognised:) - name:@"notification_restored_issue_not_recognised"]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(receiveBookProtocolNotification:) - name:@"notification_book_protocol" - object:nil]; - - [[SKPaymentQueue defaultQueue] addTransactionObserver:purchasesManager]; - } - api = [BKRBakerAPI sharedInstance]; issuesManager = [BKRIssuesManager sharedInstance]; notRecognisedTransactions = [[NSMutableArray alloc] init]; - _shelfStatus = [[BKRShelfStatus alloc] init]; - _issueViewControllers = [[NSMutableArray alloc] init]; _supportedOrientation = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UISupportedInterfaceOrientations"]; _bookToBeProcessed = nil; - + self.automaticallyAdjustsScrollViewInsets = NO; if ([BKRSettings sharedSettings].isNewsstand) { - [self handleRefresh:nil]; + [self configureForNewsstand]; + }else{ + [self configureWithBooks:[BKRIssuesManager localBooksList]]; } } return self; } -- (id)initWithBooks:(NSArray*)currentBooks { - self = [self init]; - if (self) { - self.issues = currentBooks; +- (void)configureForNewsstand { + purchasesManager = [BKRPurchasesManager sharedInstance]; + purchasesManager.delegate = self; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(receiveBookProtocolNotification:) + name:@"notification_book_protocol" + object:nil]; + [[SKPaymentQueue defaultQueue] addTransactionObserver:purchasesManager]; +} - NSMutableArray *controllers = [NSMutableArray array]; - for (BKRIssue *issue in self.issues) { - BKRIssueViewController *controller = [self createIssueViewControllerWithIssue:issue]; - [controllers addObject:controller]; - } - self.issueViewControllers = [NSMutableArray arrayWithArray:controllers]; - } - return self; +- (void)configureWithBooks:(NSArray*)currentBooks { + self.issues = currentBooks; } #pragma mark - View lifecycle @@ -121,22 +91,14 @@ - (id)initWithBooks:(NSArray*)currentBooks { - (void)viewDidLoad { [super viewDidLoad]; + // Configure navigation bar self.navigationItem.title = NSLocalizedString(@"SHELF_NAVIGATION_TITLE", nil); - self.layout = [[BKRShelfViewLayout alloc] initWithSticky:[[BKRSettings sharedSettings].issuesShelfOptions[@"headerSticky"] boolValue] - stretch:[[BKRSettings sharedSettings].issuesShelfOptions[@"headerStretch"] boolValue]]; - + // Configure shelf layout [self.layout setHeaderReferenceSize:[self getBannerSize]]; self.layout.minimumInteritemSpacing = 0; self.layout.minimumLineSpacing = 0; - self.gridView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout]; - self.gridView.dataSource = self; - self.gridView.delegate = self; - self.gridView.backgroundColor = [UIColor clearColor]; - self.gridView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; - - [self.gridView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"]; [self.gridView registerClass:[BKRShelfHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headerIdentifier"]; NSString *backgroundFillStyle = [BKRSettings sharedSettings].issuesShelfOptions[@"backgroundFillStyle"]; @@ -157,26 +119,18 @@ - (void)viewDidLoad { }else if([backgroundFillStyle isEqualToString:@"Color"]) { self.gridView.backgroundColor = [BKRUtils colorWithHexString:[BKRSettings sharedSettings].issuesShelfOptions[@"backgroundFillColor"]]; } - - [self.view addSubview:self.gridView]; - [self willRotateToInterfaceOrientation:self.interfaceOrientation duration:0]; [self.gridView reloadData]; + // Hide Buttons + self.categoryButton.hidden = true; + self.subscribeButton.hidden = true; + self.refreshButton.hidden = true; + if ([BKRSettings sharedSettings].isNewsstand) { - self.refreshButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh - target:self - action:@selector(handleRefresh:)]; - - self.subscribeButton = [[UIBarButtonItem alloc] - initWithTitle: NSLocalizedString(@"SUBSCRIBE_BUTTON_TEXT", nil) - style:UIBarButtonItemStylePlain - target:self - action:@selector(handleSubscribeButtonPressed:)]; - - self.categoryItem = [[BKRCategoryFilterItem alloc] initWithCategories:issuesManager.categories delegate:self]; - + self.refreshButton.hidden = false; + [self.subscribeButton setTitle:NSLocalizedString(@"SUBSCRIBE_BUTTON_TEXT", nil) forState:UIControlStateNormal]; + [self.categoryButton setCategories:issuesManager.categories delegate:self]; self.blockingProgressView = [[UIAlertView alloc] initWithTitle:@"Processing..." message:@"\n" @@ -193,28 +147,24 @@ - (void)viewDidLoad { [subscriptions addObject:[BKRSettings sharedSettings].freeSubscriptionProductId]; } [purchasesManager retrievePricesFor:subscriptions andEnableFailureNotifications:NO]; - + + [self handleRefresh:nil]; } + } - (void)viewWillAppear:(BOOL)animated { - - [super viewWillAppear:animated]; - [self.navigationController.navigationBar setTranslucent:NO]; - [self willRotateToInterfaceOrientation:self.interfaceOrientation duration:0]; - - for (BKRIssueViewController *controller in self.issueViewControllers) { - controller.issue.transientStatus = BakerIssueTransientStatusNone; - [controller refresh]; - } + [self.gridView reloadData]; + [self.gridView layoutSubviews]; + [self.layout invalidateLayout]; if ([BKRSettings sharedSettings].isNewsstand) { - NSMutableArray *buttonItems = [NSMutableArray arrayWithObject:self.refreshButton]; if ([purchasesManager hasSubscriptions] || [issuesManager hasProductIDs]) { - [buttonItems addObject:self.subscribeButton]; + self.subscribeButton.hidden = false; + }else{ + self.subscribeButton.hidden = true; } - self.navigationItem.leftBarButtonItems = buttonItems; // Remove limbo transactions // take current payment queue @@ -227,16 +177,11 @@ - (void)viewWillAppear:(BOOL)animated { [[SKPaymentQueue defaultQueue] addTransactionObserver:purchasesManager]; } - // Add info button - UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight]; - [infoButton addTarget:self action:@selector(handleInfoButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - self.infoItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton]; - - // Remove file info.html if you don't want the info button to be added to the shelf navigation bar - NSString *infoPath = [[NSBundle mainBundle] pathForResource:@"info" ofType:@"html" inDirectory:@"info"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:infoPath]) { - self.navigationItem.rightBarButtonItem = self.infoItem; - } + [super viewWillAppear:animated]; +} + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { + [self.layout invalidateLayout]; } - (void)viewDidAppear:(BOOL)animated { @@ -258,40 +203,6 @@ - (BOOL)shouldAutorotate { return YES; } -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - realInterfaceOrientation = toInterfaceOrientation; - [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; -} - -- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; - - // Update label widths - [self.issueViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [(BKRIssueViewController*)obj refreshContentWithCache:NO]; - }]; -} - -- (void)viewDidLayoutSubviews { - // Update gradient background (if set) - if(self.gradientLayer) { - [self.gradientLayer setFrame:self.gridView.bounds]; - } - - // Update header size - [self.layout setHeaderReferenceSize:[self getBannerSize]]; - - // Invalidate layout - [self.layout invalidateLayout]; - -} - -- (BKRIssueViewController*)createIssueViewControllerWithIssue:(BKRIssue*)issue { - BKRIssueViewController *controller = [[BKRIssueViewController alloc] initWithBakerIssue:issue]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleReadIssue:) name:@"read_issue_request" object:controller]; - return controller; -} - - (BOOL)prefersStatusBarHidden { return NO; } @@ -303,34 +214,27 @@ - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView } - (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section { - return [self.issueViewControllers count]; + return [self.issues count]; } - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath { - CGSize cellSize = [BKRIssueViewController getIssueCellSizeForOrientation:self.interfaceOrientation]; - CGRect cellFrame = CGRectMake(0, 0, cellSize.width, cellSize.height); - - static NSString *cellIdentifier = @"cellIdentifier"; - UICollectionViewCell* cell = [self.gridView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath]; - if (cell == nil) { - UICollectionViewCell* cell = [[UICollectionViewCell alloc] initWithFrame:cellFrame]; - cell.contentView.backgroundColor = [UIColor clearColor]; - cell.backgroundColor = [UIColor clearColor]; - } - - BKRIssueViewController *controller = (self.issueViewControllers)[indexPath.row]; - UIView *removableIssueView = [cell.contentView viewWithTag:42]; - if (removableIssueView) { - [removableIssueView removeFromSuperview]; - } - [cell.contentView addSubview:controller.view]; - + BKRIssueCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"IssueCell" forIndexPath:indexPath]; + BKRIssue *issue = self.issues[indexPath.row]; + [cell setIssue:issue]; + issue.delegate = cell; + [cell updateView]; return cell; } - - (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath { - return [BKRIssueViewController getIssueCellSizeForOrientation:realInterfaceOrientation]; + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + CGFloat screenWidth = [[UIScreen mainScreen] bkrWidthForOrientation:orientation]; + int cellHeight = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 240 : 190; + if (screenWidth > 700) { + return CGSizeMake(screenWidth/2, cellHeight); + } else { + return CGSizeMake(screenWidth, cellHeight); + } } - (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView viewForSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath { @@ -344,8 +248,8 @@ - (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionV return [self getBannerSize]; } -- (void)handleRefresh:(NSNotification*)notification { - [self setrefreshButtonEnabled:NO]; +- (IBAction)handleRefresh:(id)sender { + [self setRefreshButtonEnabled:NO]; MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; hud.labelText = NSLocalizedString(@"Loading", @""); @@ -354,13 +258,13 @@ - (void)handleRefresh:(NSNotification*)notification { if(status) { // Set dropdown categories - self.categoryItem.categories = issuesManager.categories; + self.categoryButton.categories = issuesManager.categories; // Show / Hide category button if(issuesManager.categories.count == 0) { - self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:self.infoItem, nil]; + self.categoryButton.hidden = true; }else{ - self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:self.infoItem, self.categoryItem, nil]; + self.categoryButton.hidden = false; } // Set issues @@ -374,7 +278,7 @@ - (void)handleRefresh:(NSNotification*)notification { message:NSLocalizedString(@"INTERNET_CONNECTION_UNAVAILABLE_MESSAGE", nil) buttonTitle:NSLocalizedString(@"INTERNET_CONNECTION_UNAVAILABLE_CLOSE", nil)]; - [self setrefreshButtonEnabled:YES]; + [self setRefreshButtonEnabled:YES]; dispatch_async(dispatch_get_main_queue(), ^{ [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; @@ -384,18 +288,6 @@ - (void)handleRefresh:(NSNotification*)notification { }]; } -- (BKRIssueViewController*)issueViewControllerWithID:(NSString*)ID { - __block BKRIssueViewController* foundController = nil; - [self.issueViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - BKRIssueViewController *ivc = (BKRIssueViewController*)obj; - if ([ivc.issue.ID isEqualToString:ID]) { - foundController = ivc; - *stop = YES; - } - }]; - return foundController; -} - - (BKRIssue*)bakerIssueWithID:(NSString*)ID { __block BKRIssue *foundIssue = nil; [self.issues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { @@ -409,73 +301,29 @@ - (BKRIssue*)bakerIssueWithID:(NSString*)ID { } - (void)refreshIssueList { + // Load shelf status + [self.shelfStatus load]; + NSMutableArray *filteredIssues = [NSMutableArray array]; - // Filter issues - __block NSMutableArray *filteredIssues = [NSMutableArray array]; - [issuesManager.issues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - BKRIssue *issue = (BKRIssue *)obj; + // Prepare issues + for (BKRIssue *issue in issuesManager.issues) { - // Test if category exists - if([self.categoryItem.title isEqualToString:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil)] || [issue.categories containsObject:self.categoryItem.title]) { + // Update prices + issue.price = [self.shelfStatus priceFor:issue.productID]; + + // Filter issues + NSString *categoryButtonTitle = [self.categoryButton titleForState:UIControlStateNormal]; + if([categoryButtonTitle isEqualToString:NSLocalizedString(@"ALL_CATEGORIES_TITLE", nil)] || [issue.categories containsObject:categoryButtonTitle]) { [filteredIssues addObject:issue]; } - }]; + } // Assign filtered issues self.issues = [filteredIssues copy]; - [self.shelfStatus load]; - for (BKRIssue *issue in self.issues) { - issue.price = [self.shelfStatus priceFor:issue.productID]; - } - - void (^updateIssues)() = ^{ - // Step 1: remove controllers for issues that no longer exist - __weak NSMutableArray *discardedControllers = [NSMutableArray array]; - [self.issueViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - BKRIssueViewController *ivc = (BKRIssueViewController*)obj; - - if (![self bakerIssueWithID:ivc.issue.ID]) { - [discardedControllers addObject:ivc]; - [self.gridView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:idx inSection:0]]]; - } - }]; - [self.issueViewControllers removeObjectsInArray:discardedControllers]; - - // Step 2: add controllers for issues that did not yet exist (and refresh the ones that do exist) - [self.issues enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - // NOTE: this block changes the issueViewController array while looping - BKRIssue *issue = (BKRIssue*)obj; - - BKRIssueViewController *existingIvc = [self issueViewControllerWithID:issue.ID]; - - if (existingIvc) { - existingIvc.issue = issue; - } else { - BKRIssueViewController *newIvc = [self createIssueViewControllerWithIssue:issue]; - [self.issueViewControllers insertObject:newIvc atIndex:idx]; - [self.gridView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:idx inSection:0]] ]; - } - }]; - - [self.gridView reloadData]; - }; - - // When first launched, the grid is not initialised, so we can't - // call in the "batch update" method of the grid view - if (self.gridView) { - [self.gridView performBatchUpdates:updateIssues completion:nil]; - } - else { - updateIssues(); - } - + // Update purchases [purchasesManager retrievePurchasesFor:[issuesManager productIDs] withCallback:^(NSDictionary *purchases) { - // List of purchases has been returned, so we can refresh all issues - [self.issueViewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [(BKRIssueViewController*)obj refreshWithCache:NO]; - }]; - [self setrefreshButtonEnabled:YES]; + [self setRefreshButtonEnabled:YES]; dispatch_async(dispatch_get_main_queue(), ^{ [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; @@ -483,15 +331,19 @@ - (void)refreshIssueList { }]; [purchasesManager retrievePricesFor:issuesManager.productIDs andEnableFailureNotifications:NO]; + + // Prepare grid view + [self.gridView reloadData]; + } #pragma mark - Store Kit -- (void)handleSubscribeButtonPressed:(NSNotification*)notification { +- (IBAction)handleSubscribeButtonPressed:(id)sender { if (self.subscriptionsActionSheet.visible) { [self.subscriptionsActionSheet dismissWithClickedButtonIndex:(self.subscriptionsActionSheet.numberOfButtons - 1) animated:YES]; } else { self.subscriptionsActionSheet = [self buildSubscriptionsActionSheet]; - [self.subscriptionsActionSheet showFromBarButtonItem:self.subscribeButton animated:YES]; + [self.subscriptionsActionSheet showFromRect:self.subscribeButton.frame inView:self.subscribeButton.superview animated:YES]; } } @@ -568,131 +420,6 @@ - (void) actionSheet:(UIActionSheet*)actionSheet clickedButtonAtIndex:(NSInteger } } -- (void)handleRestoreFailed:(NSNotification*)notification { - NSError *error = (notification.userInfo)[@"error"]; - [BKRUtils showAlertWithTitle:NSLocalizedString(@"RESTORE_FAILED_TITLE", nil) - message:[error localizedDescription] - buttonTitle:NSLocalizedString(@"RESTORE_FAILED_CLOSE", nil)]; - - [self.blockingProgressView dismissWithClickedButtonIndex:0 animated:YES]; - -} - -- (void)handleMultipleRestores:(NSNotification*)notification { - if ([BKRSettings sharedSettings].isNewsstand) { - - if ([notRecognisedTransactions count] > 0) { - NSSet *productIDs = [NSSet setWithArray:[[notRecognisedTransactions valueForKey:@"payment"] valueForKey:@"productIdentifier"]]; - NSString *productsList = [[productIDs allObjects] componentsJoinedByString:@", "]; - - [BKRUtils showAlertWithTitle:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_TITLE", nil) - message:[NSString stringWithFormat:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_MESSAGE", nil), productsList] - buttonTitle:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_CLOSE", nil)]; - - for (SKPaymentTransaction *transaction in notRecognisedTransactions) { - [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; - } - [notRecognisedTransactions removeAllObjects]; - } - } - - [self handleRefresh:nil]; - [self.blockingProgressView dismissWithClickedButtonIndex:0 animated:YES]; -} - -- (void)handleRestoredIssueNotRecognised:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - [notRecognisedTransactions addObject:transaction]; -} - -// TODO: this can probably be removed -- (void)handleSubscription:(NSNotification*)notification { - [self setSubscribeButtonEnabled:NO]; - [purchasesManager purchase:[BKRSettings sharedSettings].freeSubscriptionProductId]; -} - -- (void)handleSubscriptionPurchased:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - - [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; - [self setSubscribeButtonEnabled:YES]; - - if ([purchasesManager finishTransaction:transaction]) { - if (!purchasesManager.subscribed) { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_TITLE", nil) - message:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_MESSAGE", nil) - buttonTitle:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_CLOSE", nil)]; - - [self handleRefresh:nil]; - } - } else { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_TITLE", nil) - message:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_MESSAGE", nil) - buttonTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_CLOSE", nil)]; - } -} - -- (void)handleSubscriptionFailed:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - - // Show an error, unless it was the user who cancelled the transaction - if (transaction.error.code != SKErrorPaymentCancelled) { - [BKRUtils showAlertWithTitle:NSLocalizedString(@"SUBSCRIPTION_FAILED_TITLE", nil) - message:[transaction.error localizedDescription] - buttonTitle:NSLocalizedString(@"SUBSCRIPTION_FAILED_CLOSE", nil)]; - } - - [self setSubscribeButtonEnabled:YES]; -} - -- (void)handleSubscriptionRestored:(NSNotification*)notification { - SKPaymentTransaction *transaction = (notification.userInfo)[@"transaction"]; - - [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; - - if (![purchasesManager finishTransaction:transaction]) { - NSLog(@"Could not confirm purchase restore with remote server for %@", transaction.payment.productIdentifier); - } -} - -- (void)handleProductsRetrieved:(NSNotification*)notification { - NSSet *ids = (notification.userInfo)[@"ids"]; - BOOL issuesRetrieved = NO; - - for (NSString *productId in ids) { - if ([productId isEqualToString:[BKRSettings sharedSettings].freeSubscriptionProductId]) { - // ID is for a free subscription - [self setSubscribeButtonEnabled:YES]; - } else if ([[BKRSettings sharedSettings].autoRenewableSubscriptionProductIds containsObject:productId]) { - // ID is for an auto-renewable subscription - [self setSubscribeButtonEnabled:YES]; - } else { - // ID is for an issue - issuesRetrieved = YES; - } - } - - if (issuesRetrieved) { - NSString *price; - for (BKRIssueViewController *controller in self.issueViewControllers) { - price = [purchasesManager priceFor:controller.issue.productID]; - if (price) { - [controller setPrice:price]; - [self.shelfStatus setPrice:price for:controller.issue.productID]; - } - } - [self.shelfStatus save]; - } -} - -- (void)handleProductsRequestFailed:(NSNotification*)notification { - NSError *error = (notification.userInfo)[@"error"]; - - [BKRUtils showAlertWithTitle:NSLocalizedString(@"PRODUCTS_REQUEST_FAILED_TITLE", nil) - message:[error localizedDescription] - buttonTitle:NSLocalizedString(@"PRODUCTS_REQUEST_FAILED_CLOSE", nil)]; -} - #pragma mark - Navigation management - (void)collectionView:(UICollectionView*)collectionView didSelectItemAtIndexPath:(NSIndexPath*)indexPath { @@ -708,14 +435,10 @@ - (void)readIssue:(BKRIssue*)issue if ([status isEqual:@"opening"]) { book = [[BKRBook alloc] initWithBookPath:issue.path bundled:NO]; if (book) { - [self pushViewControllerWithBook:book]; + [self pushViewControllerWithIssue:issue]; } else { NSLog(@"[ERROR] Book %@ could not be initialized", issue.ID); - issue.transientStatus = BakerIssueTransientStatusNone; - // Let's refresh everything as it's easier. This is an edge case anyway ;) - for (BKRIssueViewController *controller in self.issueViewControllers) { - [controller refresh]; - } + issue.status = BakerIssueStatusNone; [BKRUtils showAlertWithTitle:NSLocalizedString(@"ISSUE_OPENING_FAILED_TITLE", nil) message:NSLocalizedString(@"ISSUE_OPENING_FAILED_MESSAGE", nil) buttonTitle:NSLocalizedString(@"ISSUE_OPENING_FAILED_CLOSE", nil)]; @@ -724,49 +447,57 @@ - (void)readIssue:(BKRIssue*)issue } else { if ([status isEqual:@"bundled"]) { book = [issue bakerBook]; - [self pushViewControllerWithBook:book]; + [self pushViewControllerWithIssue:issue]; } } } -- (void)handleReadIssue:(NSNotification*)notification -{ - BKRIssueViewController *controller = notification.object; - [self readIssue:controller.issue]; -} -- (void)receiveBookProtocolNotification:(NSNotification*)notification -{ + +- (void)receiveBookProtocolNotification:(NSNotification*)notification { self.bookToBeProcessed = (notification.userInfo)[@"ID"]; - [self.navigationController popToRootViewControllerAnimated:YES]; + if ([self.navigationController visibleViewController] == self) { + [self handleBookToBeProcessed]; + }else{ + [self.navigationController popToRootViewControllerAnimated:YES]; + } } -- (void)handleBookToBeProcessed -{ - for (BKRIssueViewController *issueViewController in self.issueViewControllers) { - if ([issueViewController.issue.ID isEqualToString:self.bookToBeProcessed]) { - [issueViewController actionButtonPressed:nil]; - break; +- (void)handleBookToBeProcessed { + BKRIssue *issue = [issuesManager issueWithId:self.bookToBeProcessed]; + if(issue) { + NSString *status = [issue getStatus]; + if ([status isEqualToString:@"remote"] || [status isEqualToString:@"purchased"]) { + [self issueCell:nil requestsDownloadActionForIssue:issue]; + } else if ([status isEqualToString:@"downloaded"] || [status isEqualToString:@"bundled"]) { + [self issueCell:nil requestsReadActionForIssue:issue]; + } else if ([status isEqualToString:@"purchasable"]) { + [self issueCell:nil requestsPurchaseActionForIssue:issue]; } } - self.bookToBeProcessed = nil; } -- (void)pushViewControllerWithBook:(BKRBook*)book -{ - BKRBookViewController *bakerViewController = [[BKRBookViewController alloc] initWithBook:book]; - [self.navigationController pushViewController:bakerViewController animated:YES]; +- (void)pushViewControllerWithIssue:(BKRIssue*)issue { + self.issueToBeRead = issue; + [self performSegueWithIdentifier:@"ShowBookSegue" sender:nil]; +} + +-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if ([[segue identifier] isEqualToString:@"ShowBookSegue"]) { + BKRBookViewController *bookViewController = [segue destinationViewController]; + [bookViewController configureWithIssue:self.issueToBeRead]; + } } #pragma mark - Buttons management -- (void)setrefreshButtonEnabled:(BOOL)enabled { +- (void)setRefreshButtonEnabled:(BOOL)enabled { self.refreshButton.enabled = enabled; } - (void)setSubscribeButtonEnabled:(BOOL)enabled { self.subscribeButton.enabled = enabled; if (enabled) { - self.subscribeButton.title = NSLocalizedString(@"SUBSCRIBE_BUTTON_TEXT", nil); + [self.subscribeButton setTitle:NSLocalizedString(@"SUBSCRIBE_BUTTON_TEXT", nil) forState:UIControlStateNormal]; } else { - self.subscribeButton.title = NSLocalizedString(@"SUBSCRIBE_BUTTON_DISABLED_TEXT", nil); + [self.subscribeButton setTitle:NSLocalizedString(@"SUBSCRIBE_BUTTON_DISABLED_TEXT", nil) forState:UIControlStateNormal]; } } @@ -776,7 +507,7 @@ - (void)webViewDidFinishLoad:(UIWebView*)webView { @"app_id": [BKRUtils appID]}]; } -- (void)handleInfoButtonPressed:(id)sender { +- (IBAction)handleInfoButtonPressed:(id)sender { // If the button is pressed when the info box is open, close it if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { @@ -802,9 +533,7 @@ - (void)handleInfoButtonPressed:(id)sender { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { // On iPad use the UIPopoverController infoPopover = [[UIPopoverController alloc] initWithContentViewController:popoverContent]; - [infoPopover presentPopoverFromBarButtonItem:self.infoItem - permittedArrowDirections:UIPopoverArrowDirectionUp - animated:YES]; + [infoPopover presentPopoverFromRect:self.infoButton.frame inView:self.infoButton.superview permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES]; } else { // On iPhone push the view controller to the navigation [self.navigationController pushViewController:popoverContent animated:YES]; @@ -814,15 +543,6 @@ - (void)handleInfoButtonPressed:(id)sender { #pragma mark - Helper methods -- (void)addPurchaseObserver:(SEL)notificationSelector name:(NSString*)notificationName { - if ([BKRSettings sharedSettings].isNewsstand) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:notificationSelector - name:notificationName - object:purchasesManager]; - } -} - - (int)getBannerHeight { return [[BKRSettings sharedSettings].issuesShelfOptions[[NSString stringWithFormat:@"headerHeight%@%@", [self getDeviceString], [self getOrientationString]]] intValue]; } @@ -841,8 +561,208 @@ - (NSString *)getOrientationString { #pragma mark - BKRCategoryFilterItemDelegate -- (void)categoryFilterItem:(BKRCategoryFilterItem *)categoryFilterItem clickedAction:(NSString *)action { +- (void)categoryFilterButton:(BKRCategoryFilterButton *)categoryFilterButton clickedAction:(NSString *)action { [self refreshIssueList]; } +#pragma mark - BKRIssueCellDelegate + +- (void)issueCell:(BKRIssueCell *)cell requestsReadActionForIssue:(BKRIssue *)issue { + [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueOpen" object:self]; // -> Baker Analytics Event + issue.status = BakerIssueStatusOpening; + [self readIssue:issue]; + [cell updateView]; +} + +- (void)issueCell:(BKRIssueCell *)cell requestsDownloadActionForIssue:(BKRIssue *)issue { + if ([BKRSettings sharedSettings].isNewsstand) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueDownload" object:self]; // -> Baker Analytics Event + [issue download]; + [cell updateView]; + } +} + +- (void)issueCell:(BKRIssueCell *)cell requestsPurchaseActionForIssue:(BKRIssue *)issue { + if ([BKRSettings sharedSettings].isNewsstand) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssuePurchase" object:self]; // -> Baker Analytics Event + if (![[BKRPurchasesManager sharedInstance] purchase:issue.productID]) { + // Still retrieving SKProduct: delay purchase + issue.purchaseDelayed = true; + [[BKRPurchasesManager sharedInstance] retrievePriceFor:issue.productID]; + issue.status = BakerIssueStatusUnpriced; + } else { + issue.status = BakerIssueStatusPurchasing; + } + [cell updateView]; + } + +} + +- (void)issueCell:(BKRIssueCell *)cell requestsArchiveActionForIssue:(BKRIssue *)issue { + [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueArchive" object:self]; // -> Baker Analytics Event + NKLibrary *nkLib = [NKLibrary sharedLibrary]; + NKIssue *nkIssue = [nkLib issueWithName:issue.ID]; + NSString *name = nkIssue.name; + NSDate *date = nkIssue.date; + [nkLib removeIssue:nkIssue]; + nkIssue = [nkLib addIssueWithName:name date:date]; + issue.path = [[nkIssue contentURL] path]; + [cell updateView]; +} + +#pragma mark - BKRPurchasesManagerDelegate + +- (void)purchasesManager:(BKRPurchasesManager *)manager retrievedProductIds:(NSMutableSet *)productIds { + BOOL issuesRetrieved = NO; + NSString *price; + for (NSString *productId in productIds) { + if ([productId isEqualToString:[BKRSettings sharedSettings].freeSubscriptionProductId]) { + // ID is for a free subscription + [self setSubscribeButtonEnabled:YES]; + } else if ([[BKRSettings sharedSettings].autoRenewableSubscriptionProductIds containsObject:productId]) { + // ID is for an auto-renewable subscription + [self setSubscribeButtonEnabled:YES]; + } else { + // ID is for an issue + issuesRetrieved = YES; + BKRIssue *issue = [issuesManager issueWithProductId:productId]; + if(issue) { + price = [manager priceFor:issue.productID]; + [self.shelfStatus setPrice:price for:issue.productID]; + issue.price = price; + [issue dataChanged]; + } + } + } + + if (issuesRetrieved) { + [self.shelfStatus save]; + } +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager productsRequestFailedWithError:(NSError *)error { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"PRODUCTS_REQUEST_FAILED_TITLE", nil) + message:[error localizedDescription] + buttonTitle:NSLocalizedString(@"PRODUCTS_REQUEST_FAILED_CLOSE", nil)]; +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager purchasedSubscriptionWithTransaction:(SKPaymentTransaction *)transaction { + [manager markAsPurchased:transaction.payment.productIdentifier]; + [self setSubscribeButtonEnabled:YES]; + if ([purchasesManager finishTransaction:transaction]) { + if (!purchasesManager.subscribed) { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_TITLE", nil) + message:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_MESSAGE", nil) + buttonTitle:NSLocalizedString(@"SUBSCRIPTION_SUCCESSFUL_CLOSE", nil)]; + + [self handleRefresh:nil]; + } + } else { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_TITLE", nil) + message:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_MESSAGE", nil) + buttonTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_CLOSE", nil)]; + } +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager subscriptionFailedForTransaction:(SKPaymentTransaction *)transaction { + // Show an error, unless it was the user who cancelled the transaction + if (transaction.error.code != SKErrorPaymentCancelled) { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"SUBSCRIPTION_FAILED_TITLE", nil) + message:[transaction.error localizedDescription] + buttonTitle:NSLocalizedString(@"SUBSCRIPTION_FAILED_CLOSE", nil)]; + } + [self setSubscribeButtonEnabled:YES]; +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager subscriptionRestoredForTransaction:(SKPaymentTransaction *)transaction { + [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; + if (![purchasesManager finishTransaction:transaction]) { + NSLog(@"Could not confirm purchase restore with remote server for %@", transaction.payment.productIdentifier); + } +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager productRestoredForTransaction:(SKPaymentTransaction *)transaction { + + // Find issue by product id + BKRIssue *issue = [issuesManager issueWithProductId:transaction.payment.productIdentifier]; + if (issue) { + [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; + if (![purchasesManager finishTransaction:transaction]) { + NSLog(@"[BakerShelf] Could not confirm purchase restore with remote server for %@", transaction.payment.productIdentifier); + } + issue.status = BakerIssueStatusNone; + [issue dataChanged]; + } +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager restoreFailedWithError:(NSError *)error { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"RESTORE_FAILED_TITLE", nil) + message:[error localizedDescription] + buttonTitle:NSLocalizedString(@"RESTORE_FAILED_CLOSE", nil)]; + [self.blockingProgressView dismissWithClickedButtonIndex:0 animated:YES]; +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager handleMultipleRestores:(NSArray *)transactions { + if ([BKRSettings sharedSettings].isNewsstand) { + if ([notRecognisedTransactions count] > 0) { + NSSet *productIDs = [NSSet setWithArray:[[notRecognisedTransactions valueForKey:@"payment"] valueForKey:@"productIdentifier"]]; + NSString *productsList = [[productIDs allObjects] componentsJoinedByString:@", "]; + [BKRUtils showAlertWithTitle:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_TITLE", nil) + message:[NSString stringWithFormat:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_MESSAGE", nil), productsList] + buttonTitle:NSLocalizedString(@"RESTORED_ISSUE_NOT_RECOGNISED_CLOSE", nil)]; + + for (SKPaymentTransaction *transaction in notRecognisedTransactions) { + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } + [notRecognisedTransactions removeAllObjects]; + } + } + [self handleRefresh:nil]; + [self.blockingProgressView dismissWithClickedButtonIndex:0 animated:YES]; +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager handleNotRecognizedTransaction:(SKPaymentTransaction *)transaction { + [notRecognisedTransactions addObject:transaction]; +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager purchasedProductWithTransaction:(SKPaymentTransaction *)transaction { + + // Find issue by product id + BKRIssue *issue = [issuesManager issueWithProductId:transaction.payment.productIdentifier]; + if (issue) { + [purchasesManager markAsPurchased:transaction.payment.productIdentifier]; + if ([purchasesManager finishTransaction:transaction]) { + if (!transaction.originalTransaction) { + // Do not show alert on restoring a transaction + [BKRUtils showAlertWithTitle:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_TITLE", nil) + message:[NSString stringWithFormat:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_MESSAGE", nil), issue.title] + buttonTitle:NSLocalizedString(@"ISSUE_PURCHASE_SUCCESSFUL_CLOSE", nil)]; + } + } else { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_TITLE", nil) + message:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_MESSAGE", nil) + buttonTitle:NSLocalizedString(@"TRANSACTION_RECORDING_FAILED_CLOSE", nil)]; + } + + issue.status = BakerIssueStatusNone; + [purchasesManager retrievePurchasesFor:[NSSet setWithObject:issue.productID] withCallback:^(NSDictionary *purchases) { + [issue dataChanged]; + }]; + } + +} + +- (void)purchasesManager:(BKRPurchasesManager *)manager productPurchaseFailedForTransaction:(SKPaymentTransaction *)transaction { + BKRIssue *issue = [issuesManager issueWithProductId:transaction.payment.productIdentifier]; + if (issue) { + if (transaction.error.code != SKErrorPaymentCancelled) { + [BKRUtils showAlertWithTitle:NSLocalizedString(@"ISSUE_PURCHASE_FAILED_TITLE", nil) + message:[transaction.error localizedDescription] + buttonTitle:NSLocalizedString(@"ISSUE_PURCHASE_FAILED_CLOSE", nil)]; + } + issue.status = BakerIssueStatusNone; + [issue dataChanged]; + } +} + @end diff --git a/BakerView/BKRBookViewController.h b/BakerView/BKRBookViewController.h index b4159166..c767f61a 100644 --- a/BakerView/BKRBookViewController.h +++ b/BakerView/BKRBookViewController.h @@ -36,6 +36,7 @@ #import "BKRIndexViewController.h" #import "BKRModalWebViewController.h" #import "BKRBook.h" +#import "BKRIssue.h" #import "BKRBookStatus.h" @class Downloader; @@ -112,16 +113,19 @@ #pragma mark - Properties @property (nonatomic, strong) BKRBook *book; -@property (nonatomic, strong) UIScrollView *scrollView; +@property (nonatomic, strong) BKRIssue *issue; +@property (nonatomic, strong) IBOutlet UIScrollView *scrollView; @property (nonatomic, strong) UIWebView *currPage; -@property (nonatomic, strong) UIBarButtonItem *shareButton; +@property (nonatomic, strong) IBOutlet UIBarButtonItem *shareButton; @property (nonatomic, assign) int currentPageNumber; @property (nonatomic, assign) BOOL barsHidden; #pragma mark - Initialization -- (id)initWithBook:(BKRBook*)bakerBook; +- (id)initWithIssue:(BKRIssue *)issue; +- (void)configureWithIssue:(BKRIssue *)issue; + - (BOOL)loadBookWithBookPath:(NSString*)bookPath; - (void)cleanupBookEnvironment; - (void)resetPageSlots; @@ -205,4 +209,8 @@ - (BOOL)isIndexView:(UIWebView*)webView; +#pragma mark - Actions + +- (IBAction)handleShareButtonPressed:(id)sender; + @end diff --git a/BakerView/BKRBookViewController.m b/BakerView/BKRBookViewController.m index 6fb85aca..83ca4e49 100755 --- a/BakerView/BKRBookViewController.m +++ b/BakerView/BKRBookViewController.m @@ -53,116 +53,109 @@ @implementation BKRBookViewController #pragma mark - INIT -- (id)initWithBook:(BKRBook *)bakerBook { +- (id)initWithIssue:(BKRIssue *)issue { self = [super init]; if (self) { - NSLog(@"[BakerView] Init book view..."); - - _book = bakerBook; - - if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { - // Only available in iOS 7 + - self.automaticallyAdjustsScrollViewInsets = NO; - } - - // ****** DEVICE SCREEN BOUNDS - screenBounds = [[UIScreen mainScreen] bounds]; - NSLog(@"[BakerView] Device Screen (WxH): %fx%f.", screenBounds.size.width, screenBounds.size.height); - - // ****** SUPPORTED ORIENTATION FROM PLIST - supportedOrientation = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UISupportedInterfaceOrientations"]; - - if (![[NSFileManager defaultManager] fileExistsAtPath:self.bkrCachePath]) { - [[NSFileManager defaultManager] createDirectoryAtPath:self.bkrCachePath withIntermediateDirectories:YES attributes:nil error:nil]; - } - - // ****** SCREENSHOTS DIRECTORY //TODO: set in load book only if is necessary - defaultScreeshotsPath = [[self.bkrCachePath stringByAppendingPathComponent:@"screenshots"] stringByAppendingPathComponent:bakerBook.ID]; - - // ****** STATUS FILE - bookStatus = [[BKRBookStatus alloc] initWithBookId:bakerBook.ID]; - NSLog(@"[BakerView] Status: page %@ @ scrollIndex %@px.", bookStatus.page, bookStatus.scrollIndex); - - // ****** Initialize audio session for html5 audio - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - BOOL ok; - NSError *setCategoryError = nil; - ok = [audioSession setCategory:AVAudioSessionCategoryPlayback - error:&setCategoryError]; - if (!ok) { - NSLog(@"[BakerView] AudioSession - %s setCategoryError=%@", __PRETTY_FUNCTION__, setCategoryError); - } - - // ****** BOOK ENVIRONMENT - pages = [NSMutableArray array]; - toLoad = [NSMutableArray array]; - - pageDetails = [NSMutableArray array]; - - attachedScreenshotPortrait = [NSMutableDictionary dictionary]; - attachedScreenshotLandscape = [NSMutableDictionary dictionary]; - - tapNumber = 0; - stackedScrollingAnimations = 0; // TODO: CHECK IF STILL USED! - - currentPageFirstLoading = YES; - currentPageIsDelayingLoading = YES; - currentPageHasChanged = NO; - currentPageIsLocked = NO; - currentPageWillAppearUnderModal = NO; - - userIsScrolling = NO; - shouldPropagateInterceptedTouch = YES; - shouldForceOrientationUpdate = YES; - - adjustViewsOnAppDidBecomeActive = NO; - _barsHidden = NO; - - webViewBackground = nil; - - pageNameFromURL = nil; - anchorFromURL = nil; - - // TODO: LOAD BOOK METHOD IN VIEW DID LOAD - [self loadBookWithBookPath:bakerBook.path]; + [self configureWithIssue:issue]; } return self; } + +- (void)configureWithIssue:(BKRIssue *)issue { + self.issue = issue; + if ([BKRSettings sharedSettings].isNewsstand) { + self.book = [[BKRBook alloc] initWithBookPath:issue.path bundled:NO]; + } else { + self.book = [issue bakerBook]; + } + NSLog(@"[BakerView] Init book view..."); + + if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { + // Only available in iOS 7 + + self.automaticallyAdjustsScrollViewInsets = NO; + } + + // ****** DEVICE SCREEN BOUNDS + screenBounds = [[UIScreen mainScreen] bounds]; + NSLog(@"[BakerView] Device Screen (WxH): %fx%f.", screenBounds.size.width, screenBounds.size.height); + + // ****** SUPPORTED ORIENTATION FROM PLIST + supportedOrientation = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UISupportedInterfaceOrientations"]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:self.bkrCachePath]) { + [[NSFileManager defaultManager] createDirectoryAtPath:self.bkrCachePath withIntermediateDirectories:YES attributes:nil error:nil]; + } + + // ****** SCREENSHOTS DIRECTORY //TODO: set in load book only if is necessary + defaultScreeshotsPath = [[self.bkrCachePath stringByAppendingPathComponent:@"screenshots"] stringByAppendingPathComponent:_book.ID]; + + // ****** STATUS FILE + bookStatus = [[BKRBookStatus alloc] initWithBookId:_book.ID]; + NSLog(@"[BakerView] Status: page %@ @ scrollIndex %@px.", bookStatus.page, bookStatus.scrollIndex); + + // ****** Initialize audio session for html5 audio + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + BOOL ok; + NSError *setCategoryError = nil; + ok = [audioSession setCategory:AVAudioSessionCategoryPlayback + error:&setCategoryError]; + if (!ok) { + NSLog(@"[BakerView] AudioSession - %s setCategoryError=%@", __PRETTY_FUNCTION__, setCategoryError); + } + + // ****** BOOK ENVIRONMENT + pages = [NSMutableArray array]; + toLoad = [NSMutableArray array]; + + pageDetails = [NSMutableArray array]; + + attachedScreenshotPortrait = [NSMutableDictionary dictionary]; + attachedScreenshotLandscape = [NSMutableDictionary dictionary]; + + tapNumber = 0; + stackedScrollingAnimations = 0; // TODO: CHECK IF STILL USED! + + currentPageFirstLoading = YES; + currentPageIsDelayingLoading = YES; + currentPageHasChanged = NO; + currentPageIsLocked = NO; + currentPageWillAppearUnderModal = NO; + + userIsScrolling = NO; + shouldPropagateInterceptedTouch = YES; + shouldForceOrientationUpdate = YES; + + adjustViewsOnAppDidBecomeActive = NO; + _barsHidden = NO; + + webViewBackground = nil; + + pageNameFromURL = nil; + anchorFromURL = nil; + + // TODO: LOAD BOOK METHOD IN VIEW DID LOAD + [self loadBookWithBookPath:_book.path]; +} + - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = self.book.title; - // ****** SET THE INITIAL SIZE FOR EVERYTHING // Avoids strange animations when opening [self setPageSize:[self getCurrentInterfaceOrientation:self.interfaceOrientation]]; // SOCIAL MEDIA INTEGRATION - START - if ([BKRSettings sharedSettings].showSocialShareButton) { - self.shareButton = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemAction - target:self - action:@selector(shareBtnAction:)]; - self.navigationItem.rightBarButtonItem = self.shareButton; + if (![BKRSettings sharedSettings].showSocialShareButton) { + [self.navigationItem setRightBarButtonItem:nil]; } // SOCIAL MEDIA INTEGRATION - END - // ****** SCROLLVIEW INIT - self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, pageWidth, pageHeight)]; - self.scrollView.showsHorizontalScrollIndicator = YES; - self.scrollView.showsVerticalScrollIndicator = NO; - self.scrollView.delaysContentTouches = NO; - self.scrollView.pagingEnabled = YES; - self.scrollView.delegate = self; - self.scrollView.scrollEnabled = [self.book.bakerPageTurnSwipe boolValue]; self.scrollView.backgroundColor = [BKRUtils colorWithHexString:self.book.bakerBackground]; - [self.view addSubview:self.scrollView]; - - // ****** BAKER BACKGROUND backgroundImageLandscape = nil; backgroundImagePortrait = nil; @@ -243,10 +236,7 @@ - (UIImage *)resizeImage:(UIImage*)image { return newImage; } - - -- (void) shareBtnAction:(UIButton *)sender { - +- (IBAction)handleShareButtonPressed:(id)sender { if (![self checkScreeshotForPage:_currentPageNumber andOrientation:[self getCurrentInterfaceOrientation:self.interfaceOrientation]]) { [self takeScreenshotFromView:_currPage forPage:_currentPageNumber andOrientation:[self getCurrentInterfaceOrientation:self.interfaceOrientation]]; } @@ -1929,6 +1919,7 @@ - (void)viewWillDisappear:(BOOL)animated { // Baker book is disappearing because it was popped from the navigation stack -> Baker book is closing [[NSNotificationCenter defaultCenter] postNotificationName:@"BakerIssueClose" object:self]; // -> Baker Analytics Event [self saveBookStatusWithScrollIndex]; + self.issue.status = BakerIssueStatusNone; } } - (void)saveBookStatusWithScrollIndex { diff --git a/BakerView/BKRShelfViewLayout.m b/BakerView/BKRShelfViewLayout.m index eab78e31..6e4db677 100644 --- a/BakerView/BKRShelfViewLayout.m +++ b/BakerView/BKRShelfViewLayout.m @@ -7,11 +7,20 @@ // #import "BKRShelfViewLayout.h" +#import "BKRSettings.h" @implementation BKRShelfViewLayout +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + _isSticky = [[BKRSettings sharedSettings].issuesShelfOptions[@"headerSticky"] boolValue]; + _isStretch = [[BKRSettings sharedSettings].issuesShelfOptions[@"headerStretch"] boolValue]; + } + return self; +} + - (id)initWithSticky:(BOOL)sticky stretch:(BOOL)stretch { - self = [super init]; if (self) { _isSticky = sticky; diff --git a/BakerView/ui/BKRStoryboard.storyboard b/BakerView/ui/BKRStoryboard.storyboard new file mode 100644 index 00000000..0a6eb624 --- /dev/null +++ b/BakerView/ui/BKRStoryboard.storyboard @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +