diff --git a/GIT_FLOW.md b/GIT_FLOW.md new file mode 100644 index 0000000..f73693b --- /dev/null +++ b/GIT_FLOW.md @@ -0,0 +1,16 @@ +# Git Flow + +## Branches +### `main` +* Contains code that is currently deployed to App Store + +### `develop` +* Contains code that has the new features implemented but not yet deployed to App Store + +### `feature` +* Contains code that are in development and is work in progress. + +## Sample Flow +* New features are developed in feature branches. +* Once a feature is complete, it is merged back into the develop branch. +* Periodically, the develop branch is merged into the main branch to create a new release. diff --git a/JCAlerts.xcodeproj/project.pbxproj b/JCAlerts.xcodeproj/project.pbxproj index b7cc277..919ea0f 100644 --- a/JCAlerts.xcodeproj/project.pbxproj +++ b/JCAlerts.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ D2153F732B32665B00F405C8 /* UKNotificationDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2153F722B32665B00F405C8 /* UKNotificationDetailView.swift */; }; D2153F752B32A55F00F405C8 /* FCMTopicDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2153F742B32A55F00F405C8 /* FCMTopicDelegate.swift */; }; D2153F772B32B33600F405C8 /* UIExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2153F762B32B33600F405C8 /* UIExt.swift */; }; + D21693AB2B3E63F20020BECF /* AppMetadataCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21693AA2B3E63F20020BECF /* AppMetadataCell.swift */; }; + D21693AD2B3EAB470020BECF /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = D21693AC2B3EAB470020BECF /* README.md */; }; D242119F2AE75893005BD72C /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D242119E2AE75893005BD72C /* Constants.swift */; }; D24211A12AE7589D005BD72C /* StringExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = D24211A02AE7589D005BD72C /* StringExt.swift */; }; D24211A52AE75972005BD72C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D24211A42AE75972005BD72C /* Settings.storyboard */; }; @@ -61,7 +63,7 @@ D2C660E52AD2073E00CB52BE /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = D2C660E42AD2073E00CB52BE /* FirebaseMessaging */; }; D2C660EC2ADA881800CB52BE /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D2C660EB2ADA881800CB52BE /* GoogleService-Info.plist */; }; D2E412DD2B26B3E9003EC53F /* NotificationComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E412DC2B26B3E9003EC53F /* NotificationComment.swift */; }; - D2E412DF2B26B7FD003EC53F /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E412DE2B26B7FD003EC53F /* UserService.swift */; }; + D2E412DF2B26B7FD003EC53F /* UserSettingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E412DE2B26B7FD003EC53F /* UserSettingService.swift */; }; D2E412E12B26DF28003EC53F /* NotificationCommentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E412E02B26DF28003EC53F /* NotificationCommentsViewController.swift */; }; D2E412E42B26E2A8003EC53F /* IQKeyboardManagerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D2E412E32B26E2A8003EC53F /* IQKeyboardManagerSwift */; }; D2E412E72B26E3BD003EC53F /* NotificationCommentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E412E52B26E3BD003EC53F /* NotificationCommentTableViewCell.swift */; }; @@ -80,6 +82,9 @@ D2153F722B32665B00F405C8 /* UKNotificationDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UKNotificationDetailView.swift; sourceTree = ""; }; D2153F742B32A55F00F405C8 /* FCMTopicDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FCMTopicDelegate.swift; sourceTree = ""; }; D2153F762B32B33600F405C8 /* UIExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIExt.swift; sourceTree = ""; }; + D21693AA2B3E63F20020BECF /* AppMetadataCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMetadataCell.swift; sourceTree = ""; }; + D21693AC2B3EAB470020BECF /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + D21693AE2B3EAB830020BECF /* GIT_FLOW.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = GIT_FLOW.md; sourceTree = ""; }; D242119E2AE75893005BD72C /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; D24211A02AE7589D005BD72C /* StringExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExt.swift; sourceTree = ""; }; D24211A42AE75972005BD72C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; @@ -113,7 +118,7 @@ D2C660EA2AD2FFEC00CB52BE /* JCAlerts.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = JCAlerts.entitlements; sourceTree = ""; }; D2C660EB2ADA881800CB52BE /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; D2E412DC2B26B3E9003EC53F /* NotificationComment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationComment.swift; sourceTree = ""; }; - D2E412DE2B26B7FD003EC53F /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; + D2E412DE2B26B7FD003EC53F /* UserSettingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingService.swift; sourceTree = ""; }; D2E412E02B26DF28003EC53F /* NotificationCommentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCommentsViewController.swift; sourceTree = ""; }; D2E412E52B26E3BD003EC53F /* NotificationCommentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCommentTableViewCell.swift; sourceTree = ""; }; D2E412E62B26E3BD003EC53F /* NotificationCommentTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NotificationCommentTableViewCell.xib; sourceTree = ""; }; @@ -160,6 +165,7 @@ children = ( D2153F662B30B2DE00F405C8 /* SettingsViewCell.swift */, D2153F6D2B30B58D00F405C8 /* NotificationCategoryCell.swift */, + D21693AA2B3E63F20020BECF /* AppMetadataCell.swift */, ); path = cells; sourceTree = ""; @@ -276,7 +282,7 @@ children = ( D24211B32AED548E005BD72C /* FCMTopicService.swift */, D2984CC92B08436A007B199C /* CloudFirestoreService.swift */, - D2E412DE2B26B7FD003EC53F /* UserService.swift */, + D2E412DE2B26B7FD003EC53F /* UserSettingService.swift */, ); path = services; sourceTree = ""; @@ -284,6 +290,8 @@ D26F6C432AD1FADF00AC1D6F = { isa = PBXGroup; children = ( + D21693AE2B3EAB830020BECF /* GIT_FLOW.md */, + D21693AC2B3EAB470020BECF /* README.md */, D26F6C4E2AD1FADF00AC1D6F /* JCAlerts */, D26F6C4D2AD1FADF00AC1D6F /* Products */, D2C660E32AD2073E00CB52BE /* Frameworks */, @@ -445,6 +453,7 @@ D26F6C5C2AD1FAE100AC1D6F /* Assets.xcassets in Resources */, D26F6C572AD1FADF00AC1D6F /* Main.storyboard in Resources */, D24211A52AE75972005BD72C /* Settings.storyboard in Resources */, + D21693AD2B3EAB470020BECF /* README.md in Resources */, D2984CD52B085321007B199C /* NotificationTableViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -458,7 +467,7 @@ files = ( D26F6C542AD1FADF00AC1D6F /* AlertsViewController.swift in Sources */, D26F6C5A2AD1FADF00AC1D6F /* JCAlerts.xcdatamodeld in Sources */, - D2E412DF2B26B7FD003EC53F /* UserService.swift in Sources */, + D2E412DF2B26B7FD003EC53F /* UserSettingService.swift in Sources */, D24211A82AEB5567005BD72C /* SettingsTableViewCell.swift in Sources */, D2153F772B32B33600F405C8 /* UIExt.swift in Sources */, D2984CD12B084A37007B199C /* NotificationPayload.swift in Sources */, @@ -482,6 +491,7 @@ D2153F632B30A4D300F405C8 /* SettingsView.swift in Sources */, D26D5F942AE757B000981481 /* SettingsViewController.swift in Sources */, D2153F6C2B30B57E00F405C8 /* NotificationCategoriesView.swift in Sources */, + D21693AB2B3E63F20020BECF /* AppMetadataCell.swift in Sources */, D26F6C522AD1FADF00AC1D6F /* SceneDelegate.swift in Sources */, D242119F2AE75893005BD72C /* Constants.swift in Sources */, D2153F672B30B2DE00F405C8 /* SettingsViewCell.swift in Sources */, @@ -646,7 +656,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = JCAlerts/JCAlerts.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2023122101; + CURRENT_PROJECT_VERSION = 2023122902; DEVELOPMENT_TEAM = 2JDGUK4DRH; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = JCAlerts/Info.plist; @@ -661,7 +671,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = io.github.johnchoi96.JCAlerts; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -679,7 +689,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = JCAlerts/JCAlerts.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2023122101; + CURRENT_PROJECT_VERSION = 2023122902; DEVELOPMENT_TEAM = 2JDGUK4DRH; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = JCAlerts/Info.plist; @@ -694,7 +704,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.1; PRODUCT_BUNDLE_IDENTIFIER = io.github.johnchoi96.JCAlerts; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/JCAlerts/controllers/NotificationDetailViewController.swift b/JCAlerts/controllers/NotificationDetailViewController.swift index ce0f360..464938b 100644 --- a/JCAlerts/controllers/NotificationDetailViewController.swift +++ b/JCAlerts/controllers/NotificationDetailViewController.swift @@ -14,6 +14,7 @@ class NotificationDetailViewController: UIViewController { @IBOutlet weak var topicNameLabel: UILabel! @IBOutlet weak var messageView: UITextView! @IBOutlet weak var timestampLabel: UILabel! + @IBOutlet weak var fontSizeStepper: UIStepper! var notificationPayload: NotificationPayload! @@ -29,6 +30,11 @@ class NotificationDetailViewController: UIViewController { messageView.text = notificationPayload.message } messageView.layer.cornerRadius = 15 + let fontSize = UserSettingService.instance.getTextViewFontSize() + messageView.font = .systemFont(ofSize: CGFloat(fontSize)) + fontSizeStepper.minimumValue = 12 + fontSizeStepper.maximumValue = 30 + fontSizeStepper.value = Double(fontSize) topicNameLabel.text = notificationPayload.topic.getTopicName() let timestamp = notificationPayload.timestamp timestampLabel.text = formatTimestamp(timestamp: timestamp) @@ -54,6 +60,18 @@ class NotificationDetailViewController: UIViewController { performSegue(withIdentifier: K.Segues.notificationDetailToComments, sender: notificationPayload) } + @IBAction func stepperTapped(_ sender: UIStepper) { + let oldFontSize = UserSettingService.instance.getTextViewFontSize() + if Double(oldFontSize) < fontSizeStepper.value { + UserSettingService.instance.increaseTextViewFontSize() + } else { + UserSettingService.instance.decreaseTextViewFontSize() + } + + let newFontSize = UserSettingService.instance.getTextViewFontSize() + messageView.font = .systemFont(ofSize: CGFloat(newFontSize)) + } + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == K.Segues.notificationDetailToComments { let vc = segue.destination as! NotificationCommentsViewController diff --git a/JCAlerts/controllers/PushNotificationDisplayViewController.swift b/JCAlerts/controllers/PushNotificationDisplayViewController.swift index cad2184..d6182ec 100644 --- a/JCAlerts/controllers/PushNotificationDisplayViewController.swift +++ b/JCAlerts/controllers/PushNotificationDisplayViewController.swift @@ -9,6 +9,8 @@ import UIKit class PushNotificationDisplayViewController: UIViewController { + @IBOutlet weak var fontSizeStepper: UIStepper! + static let nib = UINib(nibName: "PushNotificationDisplayViewController", bundle: nil) static let identifier = K.Nibs.Views.notificationDisplayView @@ -25,6 +27,11 @@ class PushNotificationDisplayViewController: UIViewController { title = "Notification Detail" textView.text = "Loading..." textView.layer.cornerRadius = 15 + let fontSize = UserSettingService.instance.getTextViewFontSize() + textView.font = .systemFont(ofSize: CGFloat(fontSize)) + fontSizeStepper.minimumValue = 12 + fontSizeStepper.maximumValue = 30 + fontSizeStepper.value = Double(fontSize) self.view.backgroundColor = UIColor(named: K.Colors.backgroundColor) cloudFirestoreService.delegate = self @@ -35,6 +42,18 @@ class PushNotificationDisplayViewController: UIViewController { } } + @IBAction func stepperTapped(_ sender: UIStepper) { + let oldFontSize = UserSettingService.instance.getTextViewFontSize() + if Double(oldFontSize) < fontSizeStepper.value { + UserSettingService.instance.increaseTextViewFontSize() + } else { + UserSettingService.instance.decreaseTextViewFontSize() + } + + let newFontSize = UserSettingService.instance.getTextViewFontSize() + textView.font = .systemFont(ofSize: CGFloat(newFontSize)) + } + @IBAction func doneTapped(_ sender: UIBarButtonItem) { self.dismiss(animated: true) } diff --git a/JCAlerts/delegates/AppDelegate.swift b/JCAlerts/delegates/AppDelegate.swift index 3d58d57..4704b5e 100644 --- a/JCAlerts/delegates/AppDelegate.swift +++ b/JCAlerts/delegates/AppDelegate.swift @@ -131,20 +131,22 @@ extension AppDelegate: UNUserNotificationCenterDelegate { -> UNNotificationPresentationOptions { let userInfo = notification.request.content.userInfo log.info("Notification Payload: \(userInfo)") + // With swizzling disabled you must let Messaging know about the message, for Analytics + // Messaging.messaging().appDidReceiveMessage(userInfo) + let notificationTopic = userInfo["notification-topic"] as! String + if !FCMTopicService.instance.topicIsSubscribed(topic: notificationTopic) { + return [] + } -#if !DEBUG + // NOTE: comment this block for development - #if !DEBUG does not work at the moment // check if notification is a test notification let isTestMessage = Bool(userInfo["test-notification"] as! String)! if isTestMessage { return [] } -#endif - - // With swizzling disabled you must let Messaging know about the message, for Analytics - // Messaging.messaging().appDidReceiveMessage(userInfo) - + // Change this to your preferred presentation option - return [[.banner, .sound]] + return [.banner, .sound] } func userNotificationCenter(_ center: UNUserNotificationCenter, diff --git a/JCAlerts/models/firebase/fcm/FCMTopic.swift b/JCAlerts/models/firebase/fcm/FCMTopic.swift index 4dafc30..c39d1a6 100644 --- a/JCAlerts/models/firebase/fcm/FCMTopic.swift +++ b/JCAlerts/models/firebase/fcm/FCMTopic.swift @@ -7,11 +7,12 @@ import Foundation -enum FCMTopic: String { +enum FCMTopic: String, CaseIterable { case ALL = "jc-alerts-all" case PETFINDER = "jc-alerts-petfinder" case METALPRICE = "jc-alerts-metalprice" + case CFB = "jc-alerts-cfb" } extension FCMTopic { @@ -24,6 +25,8 @@ extension FCMTopic { return "Petfinder" case .METALPRICE: return "MetalPrice" + case .CFB: + return "CFB" } } @@ -38,6 +41,8 @@ extension FCMTopic { return .PETFINDER } else if string == FCMTopic.METALPRICE.getTopicName() || string == FCMTopic.METALPRICE.getTopicValue() { return .METALPRICE + } else if string == FCMTopic.CFB.getTopicName() || string == FCMTopic.CFB.getTopicValue() { + return .CFB } else { return nil } diff --git a/JCAlerts/services/CloudFirestoreService.swift b/JCAlerts/services/CloudFirestoreService.swift index 7936883..6bd639f 100644 --- a/JCAlerts/services/CloudFirestoreService.swift +++ b/JCAlerts/services/CloudFirestoreService.swift @@ -19,7 +19,7 @@ class CloudFirestoreService: ObservableObject { private let logger = os.Logger() - private let userService = UserService.instance + private let userService = UserSettingService.instance var delegate: CloudFirestoreDelegate? @@ -41,7 +41,7 @@ class CloudFirestoreService: ObservableObject { var notifications: [NotificationPayload] = [] var notificationDict: [String: NotificationPayload] = [:] for document in querySnapshot!.documents { - var topic: FCMTopic + var topic: FCMTopic? switch (document.get("topic") as! String) { case FCMTopic.ALL.getTopicValue(): topic = .ALL @@ -49,9 +49,13 @@ class CloudFirestoreService: ObservableObject { topic = .PETFINDER case FCMTopic.METALPRICE.getTopicValue(): topic = .METALPRICE + case FCMTopic.CFB.getTopicValue(): + topic = .CFB default: self.logger.error("Invalid topic type") - return + } + guard let topic = topic else { + continue } // check if current topic is subscribed if !self.fcmTopicService.topicIsSubscribed(topic: topic) { @@ -69,12 +73,13 @@ class CloudFirestoreService: ObservableObject { let payload = NotificationPayload(id: UUID(), notificationTitle: notificationTitle, notificationSubtitle: notificationSubtitle, notificationId: notificationId, message: message, timestamp: timestamp.utcTimestampToDate(), topic: topic, isHtml: isHtml, isTestMessage: isTestMessage) -#if !DEBUG + + // NOTE: comment this block for development - #if !DEBUG does not work at the moment // if on prod, ignore test messages if payload.isTestMessage { continue } -#endif + notifications.append(payload) notificationDict[notificationId] = payload } @@ -104,7 +109,7 @@ class CloudFirestoreService: ObservableObject { let timestamp = document.get("timestamp") as! String let notificationTitle = document.get("notification-title") as! String let notificationSubtitle = document.get("notification-body") as! String - var topic: FCMTopic + var topic: FCMTopic? let isTestMessage = document.get("test-notification") as? Bool ?? false switch (document.get("topic") as! String) { case FCMTopic.ALL.getTopicValue(): @@ -113,8 +118,12 @@ class CloudFirestoreService: ObservableObject { topic = .PETFINDER case FCMTopic.METALPRICE.getTopicValue(): topic = .METALPRICE + case FCMTopic.CFB.getTopicValue(): + topic = .CFB default: self.logger.error("Invalid topic type") + } + guard let topic = topic else { return } let payload = NotificationPayload(id: UUID(), notificationTitle: notificationTitle, notificationSubtitle: notificationSubtitle, notificationId: notificationId, message: message, timestamp: timestamp.utcTimestampToDate(), topic: topic, isHtml: isHtml, isTestMessage: isTestMessage) diff --git a/JCAlerts/services/FCMTopicService.swift b/JCAlerts/services/FCMTopicService.swift index fb3e746..2e87f2e 100644 --- a/JCAlerts/services/FCMTopicService.swift +++ b/JCAlerts/services/FCMTopicService.swift @@ -82,6 +82,10 @@ class FCMTopicService { return getTopicsAsStrings().contains(topic.getTopicValue()) } + func topicIsSubscribed(topic: String) -> Bool { + return getTopicsAsStrings().contains(topic) + } + func restoreSubscription() { for topic in getTopicsAsStrings() { fcmInstance.subscribe(toTopic: topic) @@ -97,4 +101,10 @@ class FCMTopicService { } UserDefaults.standard.set(topicsList, forKey: TOPIC_KEY) } + + func subscribeToAllTopic() { + FCMTopic.allCases.forEach { topic in + subscribe(toTopic: topic) + } + } } diff --git a/JCAlerts/services/UserService.swift b/JCAlerts/services/UserService.swift deleted file mode 100644 index baffeb8..0000000 --- a/JCAlerts/services/UserService.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// FirebaseService.swift -// JCAlerts -// -// Created by John Choi on 12/10/23. -// - -import Foundation - -class UserService { - static let instance = UserService() - - private var currentUsername: String - - private init() { - // check if UserDefaults has username defined - let userDefaults = UserDefaults.standard - if let username = userDefaults.string(forKey: "currentUsername") { - currentUsername = username - } else { - // generate username - let uuid = UUID().uuidString - currentUsername = uuid - userDefaults.setValue(uuid, forKey: "currentUsername") - } - } - - func getCurrentUsername() -> String { - return currentUsername - } -} diff --git a/JCAlerts/services/UserSettingService.swift b/JCAlerts/services/UserSettingService.swift new file mode 100644 index 0000000..f1dd9c0 --- /dev/null +++ b/JCAlerts/services/UserSettingService.swift @@ -0,0 +1,79 @@ +// +// FirebaseService.swift +// JCAlerts +// +// Created by John Choi on 12/10/23. +// + +import Foundation + +class UserSettingService { + static let instance = UserSettingService() + + let userDefaults = UserDefaults.standard + + private var currentUsername: String + + private var textViewFontSize: Int + + private var firstTimeRunning: Bool + + private init() { + // check if UserDefaults has username defined + if let username = userDefaults.string(forKey: "currentUsername") { + currentUsername = username + } else { + // generate username + let uuid = UUID().uuidString + currentUsername = uuid + userDefaults.setValue(uuid, forKey: "currentUsername") + } + + // check if UserDefaults has textViewFontSize defined + if userDefaults.integer(forKey: "textViewFontSize") != 0 { + textViewFontSize = userDefaults.integer(forKey: "textViewFontSize") + } else { + textViewFontSize = 14 + userDefaults.setValue(textViewFontSize, forKey: "textViewFontSize") + } + + if let firstTime = userDefaults.string(forKey: "firstTimeRunning") { + firstTimeRunning = Bool(firstTime)! + } else { + firstTimeRunning = true + } + } + + func getFirstTimeRunning() -> Bool { + return firstTimeRunning + } + + func setFirstTimeRunningToFalse() { + firstTimeRunning = false + userDefaults.set("false", forKey: "firstTimeRunning") + } + + func getTextViewFontSize() -> Int { + return textViewFontSize + } + + func decreaseTextViewFontSize() { + if textViewFontSize <= 12 { + return + } + textViewFontSize -= 1 + userDefaults.setValue(textViewFontSize, forKey: "textViewFontSize") + } + + func increaseTextViewFontSize() { + if textViewFontSize >= 30 { + return + } + textViewFontSize += 1 + userDefaults.setValue(textViewFontSize, forKey: "textViewFontSize") + } + + func getCurrentUsername() -> String { + return currentUsername + } +} diff --git a/JCAlerts/storyboards/Base.lproj/Main.storyboard b/JCAlerts/storyboards/Base.lproj/Main.storyboard index 76b75e1..512ed2b 100644 --- a/JCAlerts/storyboards/Base.lproj/Main.storyboard +++ b/JCAlerts/storyboards/Base.lproj/Main.storyboard @@ -64,7 +64,7 @@ - + @@ -83,7 +83,7 @@ - + @@ -106,7 +106,7 @@ - + - + + + + + + + + + + - + + - - + @@ -176,6 +187,7 @@ + @@ -310,7 +322,7 @@ - + diff --git a/JCAlerts/storyboards/PushNotificationDisplayViewController.xib b/JCAlerts/storyboards/PushNotificationDisplayViewController.xib index f525cc2..2bff846 100644 --- a/JCAlerts/storyboards/PushNotificationDisplayViewController.xib +++ b/JCAlerts/storyboards/PushNotificationDisplayViewController.xib @@ -10,6 +10,7 @@ + @@ -19,13 +20,41 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -42,13 +71,13 @@ - - - + + - + + diff --git a/JCAlerts/swiftuis/ContentView.swift b/JCAlerts/swiftuis/ContentView.swift index ac4dcad..1fcae10 100644 --- a/JCAlerts/swiftuis/ContentView.swift +++ b/JCAlerts/swiftuis/ContentView.swift @@ -8,6 +8,13 @@ import SwiftUI struct ContentView: View { + @State private var isFirstTimeRunning = UserSettingService.instance.getFirstTimeRunning() + + private let WELCOME_MSG = "Welcome to JCAlerts!" + private let WELCOME_MSG_BODY = """ +For the first time setup, please go to the Settings tab and configure notification subscriptions +""" + var body: some View { TabView { LandingPageView() @@ -20,6 +27,19 @@ struct ContentView: View { Label("Settings", systemImage: "gear") } } + .alert(WELCOME_MSG, isPresented: $isFirstTimeRunning) { + Button(role: .cancel) { + // handle action + isFirstTimeRunning = false + FCMTopicService.instance.subscribeToAllTopic() + UserSettingService.instance.setFirstTimeRunningToFalse() + } label: { + Text("OK") + } + + } message: { + Text(WELCOME_MSG_BODY) + } } } diff --git a/JCAlerts/swiftuis/home/LandingPageView.swift b/JCAlerts/swiftuis/home/LandingPageView.swift index 0544484..e0e3af6 100644 --- a/JCAlerts/swiftuis/home/LandingPageView.swift +++ b/JCAlerts/swiftuis/home/LandingPageView.swift @@ -16,35 +16,38 @@ struct LandingPageView: View { var body: some View { NavigationView { - VStack { - ScrollView(.horizontal) { - HStack(spacing: 15) { - ForEach(cfService.trimmedNotificationPayloads) { data in - Card(payload: data) + ScrollView { + VStack { + ScrollView(.horizontal) { + HStack(spacing: 15) { + ForEach(cfService.trimmedNotificationPayloads) { data in + Card(payload: data) + } + MoreCard() + Spacer() } - MoreCard() - Spacer() + .padding() } + .scrollIndicators(.hidden) .padding() - } - .scrollIndicators(.hidden) - .padding() - NavigationLink(destination: UKAlertsView()) { - Text("More...") - .foregroundStyle(Color(K.Colors.inverseTextColor, bundle: nil)) - .font(.headline) - .frame(width: (UIScreen.current?.bounds.size.width)! * 0.75) - .padding() - .background(Color(K.Colors.backgroundColor, bundle: nil)) - .cornerRadius(15) - } - .frame(maxWidth: .infinity, alignment: .center) + NavigationLink(destination: UKAlertsView()) { + Text("More...") + .foregroundStyle(Color(K.Colors.inverseTextColor, bundle: nil)) + .font(.headline) + .frame(width: (UIScreen.current?.bounds.size.width)! * 0.75) + .padding() + .background(Color(K.Colors.backgroundColor, bundle: nil)) + .cornerRadius(15) + } + .frame(maxWidth: .infinity, alignment: .center) - Spacer() + Spacer() + } + .navigationTitle("Welcome") + .navigationBarTitleDisplayMode(.large) } - .navigationTitle("Welcome") - .navigationBarTitleDisplayMode(.large) + .scrollIndicators(.hidden) } } } diff --git a/JCAlerts/swiftuis/settings/NotificationCategoriesView.swift b/JCAlerts/swiftuis/settings/NotificationCategoriesView.swift index 12f8be8..9221b5f 100644 --- a/JCAlerts/swiftuis/settings/NotificationCategoriesView.swift +++ b/JCAlerts/swiftuis/settings/NotificationCategoriesView.swift @@ -10,7 +10,8 @@ import SwiftUI struct NotificationCategoriesView: View { private let notificationTypes = [ FCMTopic.PETFINDER, - FCMTopic.METALPRICE + FCMTopic.METALPRICE, + FCMTopic.CFB ] var body: some View { diff --git a/JCAlerts/swiftuis/settings/SettingsView.swift b/JCAlerts/swiftuis/settings/SettingsView.swift index 4718fd7..e58465a 100644 --- a/JCAlerts/swiftuis/settings/SettingsView.swift +++ b/JCAlerts/swiftuis/settings/SettingsView.swift @@ -16,10 +16,19 @@ struct SettingsView: View { var body: some View { NavigationView { List(items, id: \.self) { item in - SettingsViewCell(cellLabel: item) + Section(header: Text("Settings")) { + SettingsViewCell(cellLabel: item) + } + Section(header: Text("App Data")) { + AppMetadataCell(label: "App Version:", data: K.Device.appVersion ?? "N/A") + AppMetadataCell(label: "Build:", data: K.Device.appBuild ?? "N/A") + } + + } .navigationTitle("Settings") .navigationBarTitleDisplayMode(.large) + } } } diff --git a/JCAlerts/swiftuis/settings/cells/AppMetadataCell.swift b/JCAlerts/swiftuis/settings/cells/AppMetadataCell.swift new file mode 100644 index 0000000..79f4c49 --- /dev/null +++ b/JCAlerts/swiftuis/settings/cells/AppMetadataCell.swift @@ -0,0 +1,25 @@ +// +// AppMetadataCell.swift +// JCAlerts +// +// Created by John Choi on 12/28/23. +// + +import SwiftUI + +struct AppMetadataCell: View { + var label: String + var data: String + + var body: some View { + HStack { + Text(label) + Spacer() + Text(data) + } + } +} + +#Preview { + AppMetadataCell(label: "LABEL", data: "DATA") +} diff --git a/JCAlerts/utils/Constants.swift b/JCAlerts/utils/Constants.swift index e37adb1..45db531 100644 --- a/JCAlerts/utils/Constants.swift +++ b/JCAlerts/utils/Constants.swift @@ -35,4 +35,9 @@ struct K { struct Images { static let jcAlertsLogo = "jcalerts_logo" } + + struct Device { + static let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + static let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String + } }