Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ target 'Steps4Impact' do
pod 'FBSDKLoginKit', '~> 5.6.0'
pod 'FBSDKShareKit', '~> 5.6.0'
pod 'SnapKit', '~> 5.0.1'
pod 'SwiftLint', '~> 0.35.0'
pod 'SwiftLint', '~> 0.38.2'
pod 'FirebaseMessaging', '~> 4.1.4'
pod 'AppCenter', '~> 2.3.0'
pod 'SDWebImage', '~> 5.1.1'
Expand Down
8 changes: 4 additions & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ PODS:
- SDWebImage/Core (5.1.1)
- SnapKit (5.0.1)
- Sourcery (0.17.0)
- SwiftLint (0.35.0)
- SwiftLint (0.38.2)

DEPENDENCIES:
- AppCenter (~> 2.3.0)
Expand All @@ -123,7 +123,7 @@ DEPENDENCIES:
- SDWebImage (~> 5.1.1)
- SnapKit (~> 5.0.1)
- Sourcery (~> 0.17.0)
- SwiftLint (~> 0.35.0)
- SwiftLint (~> 0.38.2)

SPEC REPOS:
trunk:
Expand Down Expand Up @@ -190,8 +190,8 @@ SPEC CHECKSUMS:
SDWebImage: 96d7f03415ccb28d299d765f93557ff8a617abd8
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
Sourcery: 3ed61be7c8a1218fce349266139379dba477efe0
SwiftLint: 5553187048b900c91aa03552807681bb6b027846
SwiftLint: 8f5d2f903e1c9bcbc832fd16771e80a263ac6cbb

PODFILE CHECKSUM: f830b25b078bd36405ec2bfd37760619938c8f91
PODFILE CHECKSUM: 28aa85d7b93a27c9f9f6b1b8cad678e239cbd65a

COCOAPODS: 1.8.4
8 changes: 4 additions & 4 deletions Pods/Manifest.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

177 changes: 135 additions & 42 deletions Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Pods/SwiftLint/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified Pods/SwiftLint/swiftlint
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Steps4Impact.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@
26FE2451232231FB00E01B9B /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26FE2450232231FB00E01B9B /* TableViewController.swift */; };
26FE24532322332D00E01B9B /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26FE24522322332D00E01B9B /* UITableView.swift */; };
26FE2455232234A300E01B9B /* EmptyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26FE2454232234A300E01B9B /* EmptyCell.swift */; };
3A1AB4E623D812A000088D75 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1AB4E523D812A000088D75 /* Notification.swift */; };
3A1AB4E823D95D1600088D75 /* NotificationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1AB4E723D95D1600088D75 /* NotificationCell.swift */; };
3AAA32A023655CB5002B7479 /* LoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA329F23655CB5002B7479 /* LoginButton.swift */; };
3AFD516923774C4300CD3989 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3AFD516823774C4300CD3989 /* GoogleService-Info.plist */; };
7A7C5B5BCE64FE425FE48D17 /* Pods_Steps4ImpactTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 644A965BB27AB6174A19603E /* Pods_Steps4ImpactTests.framework */; };
885A50211F181C0FFD544AAA /* Pods_Steps4Impact.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E04420449F9B74A2501CBBEA /* Pods_Steps4Impact.framework */; };
A5123F1F2381B51E00C3E9DE /* JourneyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5123F1E2381B51E00C3E9DE /* JourneyViewController.swift */; };
Expand Down Expand Up @@ -304,7 +307,10 @@
26FE2454232234A300E01B9B /* EmptyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCell.swift; sourceTree = "<group>"; };
307EFC69C033D08671C5E6C6 /* Pods-steps4impact.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-steps4impact.release.xcconfig"; path = "Target Support Files/Pods-steps4impact/Pods-steps4impact.release.xcconfig"; sourceTree = "<group>"; };
31082B36DD919D9631B44E5E /* Pods-Steps4Impact.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Steps4Impact.debug.xcconfig"; path = "Target Support Files/Pods-Steps4Impact/Pods-Steps4Impact.debug.xcconfig"; sourceTree = "<group>"; };
3A1AB4E523D812A000088D75 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
3A1AB4E723D95D1600088D75 /* NotificationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCell.swift; sourceTree = "<group>"; };
3AAA329F23655CB5002B7479 /* LoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginButton.swift; sourceTree = "<group>"; };
3AFD516823774C4300CD3989 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
644A965BB27AB6174A19603E /* Pods_Steps4ImpactTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Steps4ImpactTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7D885FB78222CE09537F0C6A /* Pods-steps4impact.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-steps4impact.debug.xcconfig"; path = "Target Support Files/Pods-steps4impact/Pods-steps4impact.debug.xcconfig"; sourceTree = "<group>"; };
A5123F1E2381B51E00C3E9DE /* JourneyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JourneyViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -536,6 +542,7 @@
2611A50B2335FF930030E77E /* Supporting Files */ = {
isa = PBXGroup;
children = (
3AFD516823774C4300CD3989 /* GoogleService-Info.plist */,
26388A6A2348293700C83AC2 /* Localizable.strings */,
262BE89D2338817D0087DDDF /* Steps4Impact.entitlements */,
269E34D51E765828000726C2 /* Info.plist */,
Expand Down Expand Up @@ -572,6 +579,7 @@
2622C0952324A0490092B145 /* TableViewCell.swift */,
26A9829623CABABF00A5BD35 /* ImageButtonCell.swift */,
A53E7B40236F59F0006BD6D9 /* CollectionViewCell.swift */,
3A1AB4E723D95D1600088D75 /* NotificationCell.swift */,
);
path = Cells;
sourceTree = "<group>";
Expand Down Expand Up @@ -838,6 +846,7 @@
C9BAEECF235D5C4E00A8D7AF /* FitbitStep.swift */,
A594DC8C238B6DBA003B5EF7 /* Leaderboard.swift */,
26A9829A23CABAE800A5BD35 /* Milestone.swift */,
3A1AB4E523D812A000088D75 /* Notification.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -1032,6 +1041,7 @@
buildActionMask = 2147483647;
files = (
269E34D11E765828000726C2 /* Assets.xcassets in Resources */,
3AFD516923774C4300CD3989 /* GoogleService-Info.plist in Resources */,
269E34FD1E7658F2000726C2 /* Launch.storyboard in Resources */,
26388A682348293700C83AC2 /* Localizable.strings in Resources */,
);
Expand Down Expand Up @@ -1310,6 +1320,7 @@
F0F8EDD32210C68E00321A6D /* ProfileCard.swift in Sources */,
260BF5B02324FD31005F0882 /* SettingsViewController.swift in Sources */,
F0A0A5FE2210EC3600F20315 /* Navigation.swift in Sources */,
3A1AB4E823D95D1600088D75 /* NotificationCell.swift in Sources */,
265DE5682332D66E009DDA5A /* BadgesDataSource.swift in Sources */,
F03D77DE1EA83B0800A70230 /* Event.swift in Sources */,
F0C6D50D1EA3071A008E3B82 /* SelectionButton.swift in Sources */,
Expand All @@ -1333,6 +1344,7 @@
F078D9D52333EC3000EF0BAD /* AKFLogin.swift in Sources */,
2668B06B2361936300309B9D /* AppSecrets.generated.swift in Sources */,
F0F8EDD92210C7FA00321A6D /* ChallengeCard.swift in Sources */,
3A1AB4E623D812A000088D75 /* Notification.swift in Sources */,
26A9829B23CABAE800A5BD35 /* Milestone.swift in Sources */,
2676B3CA2331F18B00CB0A31 /* TeamSettingsHeaderCell.swift in Sources */,
2611A5082335FF290030E77E /* Service.swift in Sources */,
Expand Down
36 changes: 35 additions & 1 deletion Steps4Impact/App/AppController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ import AppCenter
import AppCenterAnalytics
import AppCenterCrashes
import HealthKit
import FirebaseCore
import FirebaseMessaging
import FirebaseInstanceID

class AppController {
class AppController: NSObject {
static let shared = AppController()

static let isTestRun = true
private override init() { }

var window: UIWindow?
var navigation: UITabBarController = Navigation()
Expand All @@ -58,6 +62,9 @@ class AppController {
// Setup Telemetry
AppEvents.activateApp()

// Initialize Firebase
FirebaseApp.configure()

// Setup Window
window?.frame = UIScreen.main.bounds
window?.rootViewController = UIViewController()
Expand Down Expand Up @@ -249,4 +256,31 @@ class AppController {
activityVC.popoverPresentationController?.sourceView = shareButton
viewController.present(activityVC, animated: true, completion: nil)
}

func registerForRemoteNotifications() {
UIApplication.shared.registerForRemoteNotifications()
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
print("Permission granted: \(granted)")
}
Messaging.messaging().delegate = self
}

func didReceivePushNotification(deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
// TODO Faisal: Save Messaging.messaging().fcmToken to current user's profile
}

func didReceivePushNotification(with userInfo: [AnyHashable: Any]) {
NotificationCenter.default.post(name: .receivedNotification, object: nil, userInfo: userInfo)
}
}

extension AppController: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
// TODO: Save to current user's profile
}

func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
NotificationCenter.default.post(name: .receivedNotification, object: nil, userInfo: remoteMessage.appData)
}
}
15 changes: 15 additions & 0 deletions Steps4Impact/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options: [UIApplication.OpenURLOptionsKey:Any]) -> Bool { // swiftlint:disable:this colon line_length
return appController.can(app, open: url, with: options)
}

func application(_ app: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
appController.didReceivePushNotification(deviceToken: deviceToken)
}

func application(_ app: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register remote notifications", error)
}

func application(_ app: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
appController.didReceivePushNotification(with: userInfo)
}
}
13 changes: 11 additions & 2 deletions Steps4Impact/App/Navigation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,19 @@ class Navigation: UITabBarController {
image: Assets.tabbarNotificationsUnselected.image,
selectedImage: Assets.tabbarNotificationsSelected.image)

// FIXME(compnerd) enumerate the notifications
self.notifications.tabBarItem.badgeValue = "0"
if let controller = notifications.viewControllers.first as? NotificationsViewController {
controller.fetchSavedNotifications()
}

self.viewControllers = [dashboard, challenge, leaderboard, notifications]

_ = NotificationCenter.default.addObserver(forName: .receivedNotification,
object: nil,
queue: nil) { [weak self] (_) in
guard let `self` = self else { return }
let oldValue = Int(self.notifications.tabBarItem.badgeValue ?? "0") ?? 0
self.notifications.tabBarItem.badgeValue = "\(oldValue + 1)"
}
}

required init?(coder aDecoder: NSCoder) {
Expand Down
7 changes: 7 additions & 0 deletions Steps4Impact/App/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ struct Strings {
static let youHaveNoNotifications = NSLocalizedString("Notifications.youHaveNoNotifications", comment: "")
}

struct NotificationsPermission {
static let title = NSLocalizedString("Notifications.Permission.title", comment: "")
static let message = NSLocalizedString("Notifications.Permission.message", comment: "")
static let proceed = NSLocalizedString("Notifications.Permission.proceed", comment: "")
static let cancel = NSLocalizedString("Notifications.Permission.cancel", comment: "")
}

struct Settings {
static let title = NSLocalizedString("Settings.title", comment: "")
static let delete = NSLocalizedString("Settings.delete", comment: "")
Expand Down
93 changes: 93 additions & 0 deletions Steps4Impact/CommonUI/Cells/NotificationCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright © 2019 Aga Khan Foundation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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 UIKit
import SnapKit

protocol NotificationCellDelegate: class {
func notificationCellTapped(context: Context?)
}

struct NotificationCellContext: CellContext {
let identifier: String = NotificationCell.identifier
let message: String
let timeDelta: String
let backgroundColor: UIColor
let context: NotificationContext

init(message: String, timeDelta: String, backgroundColor: UIColor, context: NotificationContext) {
self.message = message
self.timeDelta = timeDelta
self.backgroundColor = backgroundColor
self.context = context
}
}

enum NotificationContext: Context {
case none
case markRead(identifier: Int)
}

class NotificationCell: ConfigurableTableViewCell, Contextable {
static let identifier = "NotificationCell"

private let cardView = CardViewV2()
private let messageLabel = UILabel(typography: .bodyRegular)
private let timeDeltaLabel = UILabel(typography: .footnote)
internal var context: Context?

override func commonInit() {
super.commonInit()
timeDeltaLabel.textColor = .gray

contentView.addSubview(cardView) {
$0.leading.trailing.equalToSuperview().inset(Style.Padding.p24)
$0.top.bottom.equalToSuperview().inset(Style.Padding.p12)
}

cardView.addSubview(messageLabel) {
$0.top.equalToSuperview().inset(Style.Padding.p32)
$0.leading.trailing.equalToSuperview().inset(Style.Padding.p16)
}

cardView.addSubview(timeDeltaLabel) {
$0.leading.trailing.equalToSuperview().inset(Style.Padding.p16)
$0.top.equalTo(messageLabel.snp.bottom).offset(Style.Padding.p32)
$0.bottom.equalToSuperview().inset(Style.Padding.p32)
}
}

func configure(context: CellContext) {
guard let context = context as? NotificationCellContext else { return }
messageLabel.text = context.message
timeDeltaLabel.text = context.timeDelta
cardView.backgroundColor = context.backgroundColor
self.context = context.context
}
}
Loading