Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifications center #134

Merged
merged 45 commits into from
Oct 19, 2017
Merged
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e73f675
Add core data entity & API methods
kvld Sep 26, 2017
2dba7b5
Add empty view & presenter for notifications
kvld Sep 26, 2017
949df06
Add pages (for tests, not working yet)
kvld Sep 29, 2017
be40f31
Add section header
kvld Oct 3, 2017
e7e1353
Add notifications fetching
kvld Oct 3, 2017
7dd980d
Rename fields
kvld Oct 6, 2017
05c9229
Designed notifications cells
kvld Oct 10, 2017
10838c8
Add custom shape for AvatarImageView
kvld Oct 10, 2017
25e72a7
Add "Mark all as read" button
kvld Oct 10, 2017
278875d
Load avatars in presenter
kvld Oct 10, 2017
b126c2b
Add infinite scrolling
kvld Oct 12, 2017
6672bf3
Add delegate to handle status button click
kvld Oct 12, 2017
e98da0f
Add L10n
kvld Oct 12, 2017
9bd4326
Merge branch 'dev' into feature/notifications
kvld Oct 13, 2017
0873005
Add status button click handling
kvld Oct 13, 2017
587f664
Add "Mark all as read" button
kvld Oct 13, 2017
1c5abd1
Fix links in notifications
kvld Oct 13, 2017
b387a3a
Add deep linking on click in notification
kvld Oct 13, 2017
2b11afd
Add discussion deeplinking
kvld Oct 17, 2017
2f358cc
Update & load notifications from Core Data
kvld Oct 17, 2017
b9763f8
Move notifications to tabbar
kvld Oct 17, 2017
8be1e3a
Add left separator inset
kvld Oct 17, 2017
905cc33
Refactor code with PromiseKit
kvld Oct 17, 2017
6c805ab
Fix push notification reaction handler
kvld Oct 17, 2017
437e4a3
Add performRequest() calls
kvld Oct 17, 2017
ba9ceae
Clear cached notifications on logout
kvld Oct 17, 2017
38978f9
Sort cached notifications by time
kvld Oct 18, 2017
1bff441
Ignore /users/ links
kvld Oct 18, 2017
b2b278f
Remove Tabman, use PagerController
kvld Oct 18, 2017
0172327
Move pagination view to footer
kvld Oct 18, 2017
4e91112
Add empty state placeholder
kvld Oct 19, 2017
08f794d
Add anonymous placeholder
kvld Oct 19, 2017
f425106
Fix reload in viewDidAppear
kvld Oct 19, 2017
af84ad4
swiftlint
Ostrenkiy Oct 19, 2017
dce9ca0
Added opening deeplinks to DeepLinkRouter & removed some swift 3.2 st…
Ostrenkiy Oct 19, 2017
271e15d
removed SafeArea layout margins to support iOS 10 SDK
Ostrenkiy Oct 19, 2017
88ed1dc
Remove safe areas for pager controller
kvld Oct 19, 2017
23b120f
Fix status button reuse bug
kvld Oct 19, 2017
031a5a3
Update notification status on open
kvld Oct 19, 2017
70f8f8c
Save read status when update cached data
kvld Oct 19, 2017
727c358
Refactor localizedName properties & remove unused methods in profile
kvld Oct 19, 2017
919bf77
Re-import notification status button assets
kvld Oct 19, 2017
a248c20
Sort cached notifications by time
kvld Oct 19, 2017
b917b4a
Change 'mark all as read' behavior
kvld Oct 19, 2017
3d3a872
Merge branch 'dev' into feature/notifications
kvld Oct 19, 2017
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
Prev Previous commit
Next Next commit
Fix push notification reaction handler
kvld committed Oct 17, 2017
commit 6c805ab2c0b5c48c211a2c35c70b3ee111085e10
Binary file modified Sb/SbAppDelegate.swift
Binary file not shown.
2 changes: 1 addition & 1 deletion Stepic/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

fileprivate func handleNotification(_ notificationDict: [NSString: AnyObject]) {
if let reaction = NotificationReactionHandler().handleNotificationWithUserInfo(notificationDict),
if let reaction = NotificationReactionHandler.handle(with: notificationDict),
let topController = currentNavigation?.topViewController {
reaction(topController)
}
75 changes: 31 additions & 44 deletions Stepic/NotificationDataExtractor.swift
Original file line number Diff line number Diff line change
@@ -28,8 +28,38 @@ class NotificationDataExtractor {
return nil
}()

// Extract course id
lazy var courseId: Int? = {
guard self.type == .learn else {
return nil
}

if let courseLink = HTMLParsingUtil.getLink(self.text, index: 0) {
if let courseIdStartIndex = courseLink.lastIndexOf("-") {
let start = courseLink.characters.index(courseLink.startIndex, offsetBy: courseIdStartIndex + 1)
let end = courseLink.characters.index(courseLink.startIndex, offsetBy: courseLink.characters.count - 1)
let courseIdString = courseLink.substring(with: start..<end)
return Int(courseIdString)
}
}
return nil
}()

// Extract comments URL
lazy var commentsURL: URL? = {
guard self.type == .comments else {
return nil
}

if let commentsLink = HTMLParsingUtil.getLink(self.text, index: 2) {
let urlString = StepicApplicationsInfo.stepicURL + commentsLink
return URL(string: urlString)
} else {
return nil
}
}()

// Remove spaces and new lines
// For Comments add new line after name
lazy var preparedText: String? = {
let pText = self.text.trimmingCharacters(in: .whitespacesAndNewlines)
return pText.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty }.joined(separator: " ")
@@ -40,46 +70,3 @@ class NotificationDataExtractor {
self.type = type
}
}

/*

//gets course id if it is available for the given notification type
func getCourseId() -> Int? {
return nil
// FIXME: old notification

// if notification.type != .Learn {
// return nil
// } else {
// if let courseLink = HTMLParsingUtil.getLink(notification.htmlText, index: 0) {
// if let courseIdStartIndex = courseLink.lastIndexOf("-") {
// let start = courseLink.characters.index(courseLink.startIndex, offsetBy: courseIdStartIndex + 1)
// let end = courseLink.characters.index(courseLink.startIndex, offsetBy: courseLink.characters.count - 1)
// let courseIdString = courseLink.substring(with: start ..< end )
// return Int(courseIdString)
// }
// }
// return nil
// }
}

//gets the comments URL if it is available for the given notification type
func getCommentsURL() -> URL? {
return nil
// FIXME: old notification

// if notification.type != .Comments {
// return nil
// } else {
// if let commentsLink = HTMLParsingUtil.getLink(notification.htmlText, index: 2) {
// print("\(StepicApplicationsInfo.stepicURL)\(commentsLink)")
// let urlString = StepicApplicationsInfo.stepicURL + commentsLink
// let u = URL(string: urlString)
// print(u ?? "")
// return u
// } else {
// return nil
// }
// }
}
}*/
124 changes: 53 additions & 71 deletions Stepic/NotificationReactionHandler.swift
Original file line number Diff line number Diff line change
@@ -7,93 +7,75 @@
//

import Foundation
import SwiftyJSON

class NotificationReactionHandler {

fileprivate func deserializeObject(from userInfo: [AnyHashable: Any]) -> [String: AnyObject]? {
let jsonString = userInfo["object"] as? NSString
if let data = jsonString?.data(using: String.Encoding.utf8.rawValue) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
return json as? [String : AnyObject]
} catch {
return nil
}
} else {
return nil
fileprivate static func deserializeObject(from userInfo: [AnyHashable: Any]) -> JSON? {
if let jsonString = userInfo["object"] as? String {
return JSON(parseJSON: jsonString)
}
return nil
}

func handleNotificationWithUserInfo(_ userInfo: [AnyHashable: Any]) -> ((UIViewController) -> Void)? {

static func handle(with userInfo: [AnyHashable: Any]) -> ((UIViewController) -> Void)? {
if !AuthInfo.shared.isAuthorized {
return nil
}

if let notificationObject: [String: AnyObject] = deserializeObject(from: userInfo) {
print(notificationObject)
// FIXME: old notification usage
// if let notification = Notification(dictionary: notificationObject) {
// switch notification.type {
// case NotificationType.Learn:
// return handleLearnNotification(notification)
// case NotificationType.Comments:
// return handleCommentsNotification(notification)
// }
// }
if let json = deserializeObject(from: userInfo) {
let notification = Notification(json: json)
switch notification.type {
case .learn:
return handleLearnNotification(notification)
case .comments:
return handleCommentsNotification(notification)
default:
break
}
}
return nil
}

fileprivate func handleLearnNotification(_ notification: Notification) -> ((UIViewController) -> Void)? {
// let extractor = NotificationDataExtractor(notification: notification)
// if let courseId = extractor.getCourseId() {
//
// var course: Course? = nil
// do {
// course = try Course.getCourses([courseId])[0]
// } catch {
// print("No course with appropriate id \(courseId) found")
// return nil
// }
// let sectionsCOpt = ControllerHelper.instantiateViewController(identifier: "SectionsViewController") as? SectionsViewController
// print(sectionsCOpt ?? "")
// if let sectionsController = sectionsCOpt,
// let course = course {
// sectionsController.course = course
//
// let res: ((UIViewController) -> Void) = {
// controller in
// print("in res handler -> \(controller)")
// controller.navigationController?.pushViewController(sectionsController, animated: false)
// }
//
// return res
// }
// }
fileprivate static func handleLearnNotification(_ notification: Notification) -> ((UIViewController) -> Void)? {
let extractor = NotificationDataExtractor(text: notification.htmlText ?? "", type: notification.type)
if let courseId = extractor.courseId {
var course: Course? = nil
do {
course = try Course.getCourses([courseId])[0]
} catch {
print("handle notification: no course found, id = \(courseId)")
return nil
}

let sectionsCOpt = ControllerHelper.instantiateViewController(identifier: "SectionsViewController") as? SectionsViewController
if let sectionsController = sectionsCOpt, let course = course {
sectionsController.course = course

let res: ((UIViewController) -> Void) = { controller in
controller.navigationController?.pushViewController(sectionsController, animated: false)
}

return res
}
}
return nil
}

fileprivate func handleCommentsNotification(_ notification: Notification) -> ((UIViewController) -> Void)? {
// let extractor = NotificationDataExtractor(notification: notification)
// if let commentsURL = extractor.getCommentsURL() {
//
// let res: ((UIViewController) -> Void) = {
// controller in
//
// delay(1, closure: {
// [weak self] in
// let alert = NotificationAlertConstructor.sharedConstructor.getOpenCommentNotificationViaSafariAlertController({
// UIThread.performUI {
// WebControllerManager.sharedManager.presentWebControllerWithURL(commentsURL, inController: controller, withKey: "external link", allowsSafari: true, backButtonStyle: BackButtonStyle.close, animated: true)
// }
// })
// controller.present(alert, animated: true, completion: nil)
// })
// }
// return res
//
// }
fileprivate static func handleCommentsNotification(_ notification: Notification) -> ((UIViewController) -> Void)? {
let extractor = NotificationDataExtractor(text: notification.htmlText ?? "", type: notification.type)
if let commentsURL = extractor.commentsURL {
let res: ((UIViewController) -> Void) = { controller in
delay(1, closure: {
let alert = NotificationAlertConstructor.sharedConstructor.getOpenCommentNotificationViaSafariAlertController({
UIThread.performUI {
WebControllerManager.sharedManager.presentWebControllerWithURL(commentsURL, inController: controller, withKey: "external link", allowsSafari: true, backButtonStyle: BackButtonStyle.close, animated: true)
}
})
controller.present(alert, animated: true, completion: nil)
})
}
return res
}
return nil
}