From b0d1f13046e571ee02898c8065a99faf63ef6ebd Mon Sep 17 00:00:00 2001 From: Anthony Li Date: Wed, 27 Mar 2024 20:40:31 -0400 Subject: [PATCH] Revert "Revert "Revert April Fool's banners (#463)"" This reverts commit ae27796d0571a5476be4f7a061ea0f8427eda70b. --- PennMobile.xcodeproj/project.pbxproj | 21 ----- PennMobile/Banners/BannerDescription.swift | 24 ------ PennMobile/Banners/BannerView.swift | 72 ---------------- PennMobile/Banners/BannerViewModel.swift | 85 ------------------- .../SwiftUI/Views/Venue/DiningVenueView.swift | 4 - .../RootViewController.swift | 69 --------------- 6 files changed, 275 deletions(-) delete mode 100644 PennMobile/Banners/BannerDescription.swift delete mode 100644 PennMobile/Banners/BannerView.swift delete mode 100644 PennMobile/Banners/BannerViewModel.swift diff --git a/PennMobile.xcodeproj/project.pbxproj b/PennMobile.xcodeproj/project.pbxproj index c5c86039e..516fc1285 100644 --- a/PennMobile.xcodeproj/project.pbxproj +++ b/PennMobile.xcodeproj/project.pbxproj @@ -258,7 +258,6 @@ 897F0A932B08256C0060583A /* Toasts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897F0A922B08256C0060583A /* Toasts.swift */; }; 898DB4912B2E7AA20027CC8F /* PennForms in Frameworks */ = {isa = PBXBuildFile; productRef = 898DB4902B2E7AA20027CC8F /* PennForms */; }; 89913EAD2AE44FCE00AE30C9 /* CalendarCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89913EAC2AE44FCE00AE30C9 /* CalendarCardView.swift */; }; - 8987C8FD29CE5A8B004E9A99 /* BannerDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8987C8FC29CE5A8B004E9A99 /* BannerDescription.swift */; }; 89B454DF28E1161B00BC918B /* PathAtPennNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89B454DE28E1161B00BC918B /* PathAtPennNetworkManager.swift */; }; 89CA5FBF2AD315E400B7D3EF /* ProfileRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CA5FBE2AD315E400B7D3EF /* ProfileRowView.swift */; }; 89CA728D291721D200CF72FE /* KeychainAccessible+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89CA728C291721D200CF72FE /* KeychainAccessible+Extensions.swift */; }; @@ -271,8 +270,6 @@ 89E0DE6A2AE396E200E918FF /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E0DE692AE396E200E918FF /* HomeViewModel.swift */; }; 89E0DE6C2AE415A800E918FF /* HomeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E0DE6B2AE415A800E918FF /* HomeCardView.swift */; }; 89E37E102B6F5BD2000A86B7 /* NewListingForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E37E0F2B6F5BD2000A86B7 /* NewListingForm.swift */; }; - 89DF550A29CE49A900EF03F7 /* BannerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89DF550929CE49A900EF03F7 /* BannerViewModel.swift */; }; - 89DF550C29CE4BDF00EF03F7 /* BannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89DF550B29CE4BDF00EF03F7 /* BannerView.swift */; }; 89EA261E290EDFA7008F26CF /* CoursesDayWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89EA261C290EDF12008F26CF /* CoursesDayWidget.swift */; }; 89EA2622290EE3FD008F26CF /* CoursesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89EA2620290EE3E9008F26CF /* CoursesProvider.swift */; }; 89EA262E290F9411008F26CF /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 89EA262D290F9411008F26CF /* Intents.intentdefinition */; }; @@ -690,7 +687,6 @@ 895D98AF2ACF312300F8C5DF /* MoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreView.swift; sourceTree = ""; }; 897F0A922B08256C0060583A /* Toasts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toasts.swift; sourceTree = ""; }; 89913EAC2AE44FCE00AE30C9 /* CalendarCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarCardView.swift; sourceTree = ""; }; - 8987C8FC29CE5A8B004E9A99 /* BannerDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerDescription.swift; sourceTree = ""; }; 89B454DE28E1161B00BC918B /* PathAtPennNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathAtPennNetworkManager.swift; sourceTree = ""; wrapsLines = 0; }; 89CA5FBE2AD315E400B7D3EF /* ProfileRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileRowView.swift; sourceTree = ""; }; 89CA727229171E3900CF72FE /* DiningAnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiningAnalyticsProvider.swift; sourceTree = ""; }; @@ -704,8 +700,6 @@ 89E0DE692AE396E200E918FF /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; 89E0DE6B2AE415A800E918FF /* HomeCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCardView.swift; sourceTree = ""; }; 89E37E0F2B6F5BD2000A86B7 /* NewListingForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewListingForm.swift; sourceTree = ""; }; - 89DF550929CE49A900EF03F7 /* BannerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerViewModel.swift; sourceTree = ""; }; - 89DF550B29CE4BDF00EF03F7 /* BannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerView.swift; sourceTree = ""; }; 89EA261C290EDF12008F26CF /* CoursesDayWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoursesDayWidget.swift; sourceTree = ""; }; 89EA2620290EE3E9008F26CF /* CoursesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoursesProvider.swift; sourceTree = ""; }; 89EA262C290F2667008F26CF /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = ""; }; @@ -1007,7 +1001,6 @@ children = ( E49D3B8C2B66C6D20022AB71 /* Subletting */, 211B11A41F2995AD00A22C8A /* PennMobile.entitlements */, - 89DF550829CE499B00EF03F7 /* Banners */, CF29A15F1FB7873A0067D946 /* Onboarding */, 216640C71EBADC9D00746B8E /* General */, 21B653BC2246F31E001A97C5 /* Courses */, @@ -1731,17 +1724,6 @@ path = Views; sourceTree = ""; }; - 89DF550829CE499B00EF03F7 /* Banners */ = { - isa = PBXGroup; - children = ( - 89DF550929CE49A900EF03F7 /* BannerViewModel.swift */, - 89DF550B29CE4BDF00EF03F7 /* BannerView.swift */, - 8987C8FC29CE5A8B004E9A99 /* BannerDescription.swift */, - 8987C8FA29CE59C3004E9A99 /* UIHostingController+SafeAreaDisabling.swift */, - ); - path = Banners; - sourceTree = ""; - }; 89EA261F290EE39E008F26CF /* Courses */ = { isa = PBXGroup; children = ( @@ -2674,7 +2656,6 @@ B6040A361F8F24D900E4B783 /* AddLaundryCell.swift in Sources */, 219F01F61FB7E14B006BBC4E /* SelectionCell.swift in Sources */, 89913EAD2AE44FCE00AE30C9 /* CalendarCardView.swift in Sources */, - 89DF550A29CE49A900EF03F7 /* BannerViewModel.swift in Sources */, 2190FD351EC625BB00EC683C /* Protocols.swift in Sources */, 2189C0A82027CE4B00771C1F /* ThumbLayer.swift in Sources */, E735C9422AF81498000F7376 /* DiningSettingsView.swift in Sources */, @@ -2688,7 +2669,6 @@ 2189C0872027CDD700771C1F /* GSRNetworkManager.swift in Sources */, 2138D55522598AA800D67CA2 /* GSRTabController.swift in Sources */, 6CFA06F826E8355400944B8E /* HomeFeatureCellItem.swift in Sources */, - 89DF550C29CE4BDF00EF03F7 /* BannerView.swift in Sources */, 21640D5E20105BAC002F33CA /* HomeLaundryCell.swift in Sources */, 6CAA4B5727A763A400473CC6 /* HomePollsCell.swift in Sources */, F27AA01A23BC6D1400276C4F /* PrivacyPermissionDelegate.swift in Sources */, @@ -2697,7 +2677,6 @@ 2130A5062238C2F000DFEEC7 /* PennLoginController.swift in Sources */, 21508166220D2499002F7EA1 /* HomeNewsCellItem.swift in Sources */, 2189C0912027CE2E00771C1F /* GSRViewModel.swift in Sources */, - 8987C8FD29CE5A8B004E9A99 /* BannerDescription.swift in Sources */, C15C4B4E223EB16F00E443FD /* HomeReservationsCell.swift in Sources */, B62875F92118F95300FB2873 /* BuildingProtocol.swift in Sources */, F212BE8623B6DA8D00ED46A1 /* PrivacyTableViewCell.swift in Sources */, diff --git a/PennMobile/Banners/BannerDescription.swift b/PennMobile/Banners/BannerDescription.swift deleted file mode 100644 index 758cb06fc..000000000 --- a/PennMobile/Banners/BannerDescription.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// BannerDescription.swift -// PennMobile -// -// Created by Anthony Li on 3/24/23. -// Copyright © 2023 PennLabs. All rights reserved. -// - -struct BannerDescription: Equatable, Codable { - var image: URL - var text: String - var action: URL? -} - -struct UserEngagementMessageDescription: Codable { - var primary: String? - var secondary: String? - var actions: [Action] - - struct Action: Codable { - var url: URL - var title: String - } -} diff --git a/PennMobile/Banners/BannerView.swift b/PennMobile/Banners/BannerView.swift deleted file mode 100644 index 8d4a80201..000000000 --- a/PennMobile/Banners/BannerView.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// BannerView.swift -// PennMobile -// -// Created by Anthony Li on 3/24/23. -// Copyright © 2023 PennLabs. All rights reserved. -// - -import SwiftUI -import Kingfisher - -extension BannerDescription: View { - var imageView: some View { - KFImage(image) - .resizable() - .aspectRatio(contentMode: .fill) - .accessibilityLabel(Text(text)) - } - - var body: some View { - Group { - if let action { - Link(destination: action) { - imageView - } - } else { - imageView - } - } - .id(image) - .transition(.slide) - } -} - -struct BannerView: View { - static let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() - static let height: CGFloat = 96 - - @EnvironmentObject var viewModel: BannerViewModel - @State var banner: BannerDescription? - - func selectBanner() { - banner = viewModel.banners.random - } - - var body: some View { - Group { - if let banner { - banner - } else { - Text("Finding personalized offers for you...") - .font(.custom("Arial", size: 16)) - .foregroundColor(.uiBackground) - .padding() - } - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.primary) - .frame(height: BannerView.height) - .clipped() - .ignoresSafeArea() - .onReceive(BannerView.timer) { _ in - withAnimation { - selectBanner() - } - } - .onAppear { - viewModel.fetchBannersIfNeeded() - selectBanner() - } - } -} diff --git a/PennMobile/Banners/BannerViewModel.swift b/PennMobile/Banners/BannerViewModel.swift deleted file mode 100644 index 975301baf..000000000 --- a/PennMobile/Banners/BannerViewModel.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// BannerViewModel.swift -// PennMobile -// -// Created by Anthony Li on 3/24/23. -// Copyright © 2023 PennLabs. All rights reserved. -// - -import SwiftUI -import Foundation - -private func getDefaultBannerURL() -> URL { - let data = Data(base64Encoded: "aHR0cHM6Ly9wZW5ubGFicy5naXRodWIuaW8vcGxhdGZvcm0tc2FtcGxlLWFzc2V0cy9hc3NldHMuanNvbg==")! - return URL(string: String(data: data, encoding: .ascii)!)! -} - -class BannerViewModel: ObservableObject { - static let shared = BannerViewModel( - url: getDefaultBannerURL(), - cacheMaxAge: 60 * 60 - ) - - @Published var banners: [BannerDescription] = [] - @Published var userEngagementMessages: [UserEngagementMessageDescription] = [] - private var isFetching = false - private var lastSuccessfulFetch: Date? - - let url: URL - let cacheMaxAge: TimeInterval - - init(url: URL, cacheMaxAge: TimeInterval) { - self.url = url - self.cacheMaxAge = cacheMaxAge - } - - let decoder = { - let decoder = JSONDecoder() - decoder.allowsJSON5 = true - return decoder - }() - - func shouldDisplayBanners(on date: Date = Date()) -> Bool { - #if DEBUG - if ProcessInfo.processInfo.environment["FORCE_BANNERS"] != nil { - return true - } - #endif - - let calendar = Calendar(identifier: .gregorian) - let components = calendar.dateComponents(in: TimeZone.current, from: date) - - return components.year == 2023 && components.month == 4 && components.day! <= 2 - } - - func fetchBannersIfNeeded() { - if isFetching { - return - } - - if let lastSuccessfulFetch, -lastSuccessfulFetch.timeIntervalSinceNow < cacheMaxAge { - return - } - - struct BannerResponse: Decodable { - let assets: [BannerDescription] - let strings: [UserEngagementMessageDescription] - } - - isFetching = true - Task { - do { - let (data, _) = try await URLSession(configuration: .ephemeral).data(from: url) - let response = try decoder.decode(BannerResponse.self, from: data) - banners = response.assets - userEngagementMessages = response.strings - lastSuccessfulFetch = Date() - } catch let error { - lastSuccessfulFetch = nil - print("Failed to load banners: \(error)") - } - - isFetching = false - } - } -} diff --git a/PennMobile/Dining/SwiftUI/Views/Venue/DiningVenueView.swift b/PennMobile/Dining/SwiftUI/Views/Venue/DiningVenueView.swift index 28ccf0d93..e94666a9b 100644 --- a/PennMobile/Dining/SwiftUI/Views/Venue/DiningVenueView.swift +++ b/PennMobile/Dining/SwiftUI/Views/Venue/DiningVenueView.swift @@ -67,10 +67,6 @@ struct DiningVenueView: View { } return List { - if BannerViewModel.shared.shouldDisplayBanners() { - BannerView().environmentObject(BannerViewModel.shared) - } - Section(header: CustomHeader(name: "Dining Balance", refreshConfiguration: refreshConfiguration).environmentObject(diningAnalyticsViewModel), content: { Section(header: DiningViewHeader().environmentObject(diningAnalyticsViewModel), content: {}) }) diff --git a/PennMobile/Setup + Navigation/RootViewController.swift b/PennMobile/Setup + Navigation/RootViewController.swift index b8f218c9b..9860e8132 100755 --- a/PennMobile/Setup + Navigation/RootViewController.swift +++ b/PennMobile/Setup + Navigation/RootViewController.swift @@ -11,18 +11,11 @@ import UIKit import SwiftUI import StoreKit import SwiftyJSON -import SwiftUI // Source: https://medium.com/@stasost/ios-root-controller-navigation-3625eedbbff class RootViewController: UIViewController, NotificationRequestable, ShowsAlert { - static let userEngagementMessageDelay: TimeInterval = 60 - var current: UIViewController - var bannerController: UIHostingController? - weak var bottomConstraint: NSLayoutConstraint? - var userEngagementMessageTimer: Timer? - private var lastLoginAttempt: Date? // Fetch transactions even if hasDiningPlan() returns FALSE @@ -33,62 +26,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert super.init(nibName: nil, bundle: nil) } - func applyConstraints(to child: UIView) { - bottomConstraint = child.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor).first { - $0.firstAnchor == view.bottomAnchor || $0.secondAnchor == view.bottomAnchor - } - updateConstraintsForBanners() - } - - func updateConstraintsForBanners() { - if bannerController != nil { - bottomConstraint?.constant = -BannerView.height - } else { - bottomConstraint?.constant = 0 - } - } - - func displayBannersIfNeeded() { - if BannerViewModel.shared.shouldDisplayBanners() { - BannerViewModel.shared.fetchBannersIfNeeded() - if bannerController == nil { - bannerController = UIHostingController(rootView: AnyView( - BannerView().environmentObject(BannerViewModel.shared) - )) - bannerController!.disableSafeArea() - addChild(bannerController!) - view.addSubview(bannerController!.view) - _ = bannerController!.view.anchor(nil, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, heightConstant: BannerView.height) - - // Also set up user engagement messages - userEngagementMessageTimer = Timer.scheduledTimer(withTimeInterval: RootViewController.userEngagementMessageDelay, repeats: true) { [weak self] _ in - self?.displayUserEngagementMessage() - } - } - } - - updateConstraintsForBanners() - } - - func displayUserEngagementMessage() { - guard let message = BannerViewModel.shared.userEngagementMessages.random else { - return - } - - let alert = UIAlertController(title: message.primary, message: message.secondary, preferredStyle: .alert) - if !message.actions.isEmpty { - message.actions.forEach { action in - alert.addAction(UIAlertAction(title: action.title, style: .default) { _ in - UIApplication.shared.open(action.url) - }) - } - - alert.addAction(UIAlertAction(title: "Close", style: .cancel)) - } - - present(alert, animated: true) - } - override func viewDidLoad() { super.viewDidLoad() @@ -99,7 +36,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert addChild(current) current.view.frame = view.bounds view.addSubview(current.view) - applyConstraints(to: current.view) current.didMove(toParent: self) if #available(iOS 15, *) { @@ -136,8 +72,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert } func applicationWillEnterForeground() { - displayBannersIfNeeded() - if Account.isLoggedIn && shouldRequireLogin() { // If user is logged in but login is required, clear user data and switch to logout clearAccountData() @@ -232,7 +166,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert self.current.removeFromParent() new.didMove(toParent: self) self.current = new - self.applyConstraints(to: new.view) completion?() } } @@ -245,7 +178,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert self.current.removeFromParent() new.didMove(toParent: self) self.current = new - self.applyConstraints(to: new.view) completion?() } } @@ -254,7 +186,6 @@ class RootViewController: UIViewController, NotificationRequestable, ShowsAlert addChild(controller) controller.view.frame = view.bounds view.addSubview(controller.view) - applyConstraints(to: controller.view) controller.didMove(toParent: self) current.willMove(toParent: nil) current.view.removeFromSuperview()