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

Edit Notifications #2663

Merged
merged 207 commits into from
Oct 15, 2018
Merged
Changes from 1 commit
Commits
Show all changes
207 commits
Select commit Hold shift + click to select a range
078b517
initial RemoteNotificationsController implementation
Sep 18, 2018
5ed5db4
Merge branch 'develop' into feature/T203167
Sep 18, 2018
65cca9d
add Notification category, update names
Sep 20, 2018
bb3f142
add a new model
Sep 20, 2018
3414219
add initial model controller
Sep 20, 2018
58af848
update assertion messages
Sep 20, 2018
c53877c
add a TODO
Sep 20, 2018
32cc469
add remote notifications fetcher
Sep 20, 2018
bd85cca
Merge branch 'feature/T193691-back_end' into feature/T203167
Sep 20, 2018
5ac5947
rename operationCompletion to completion
Sep 20, 2018
a731498
add RemoteNotificationsAPIController
Sep 25, 2018
f06ed55
add operationsController to RemoteNotificationsController
Sep 25, 2018
f716eab
expand RemoteNotificationsModelController
Sep 25, 2018
2a9ad0d
remove RemoteNotificationsFetcher
Sep 25, 2018
eb0b2be
add RemoteNotificationsOperationsController
Sep 25, 2018
881ec7b
add model and Core Data classes
Sep 25, 2018
a2c801c
add initial RemoteNotificationsFetchOperation
Sep 25, 2018
ce63875
add initial RemoteNotificationsMarkAsReadOperation
Sep 25, 2018
d03a266
make model classes final
Sep 25, 2018
97d3077
add RemoteNotificationsOperation
Sep 25, 2018
0194f6a
add shouldExecute to operation
Sep 25, 2018
42533e1
project changes
Sep 25, 2018
018b96f
Merge branch 'develop' into feature/T203167
Sep 25, 2018
7580bc6
finish op if shouldn't execute
Sep 26, 2018
2071f49
remove Type and Category on decodable result for now
Sep 26, 2018
a1fcdd2
pull out Error out of Result so that other decodable structs can use it
Sep 26, 2018
79abbd6
update mark
Sep 26, 2018
9cea237
Result -> NotificationResult to distinguish between Notifications cal…
Sep 26, 2018
1462a99
add MarkReadResult
Sep 26, 2018
4d201a0
use new name in method signature
Sep 26, 2018
dcd29e2
modify getAllUnreadNotifications to accept subdomains
Sep 26, 2018
38af172
add a method to mark notifications as read
Sep 26, 2018
1e65eff
remove Wiki enum, use a dict instead
Sep 26, 2018
701bb87
make request generic, add params
Sep 26, 2018
e20a561
missing bracket
Sep 26, 2018
a547199
remove unused params
Sep 26, 2018
a529719
accept more params when creating notifications params
Sep 26, 2018
ee68709
set listOfWikis for notwikis key
Sep 26, 2018
3256acf
add a helper to method to create pipe separated list of values
Sep 26, 2018
85c33cf
add a method to create mark as read api call params
Sep 26, 2018
082c1b4
re-add mark and struct name
Sep 26, 2018
81757af
add a Category enum on RemoteNotification
Sep 26, 2018
0ca4db3
add a computed category property on RemoteNotification
Sep 26, 2018
d1ddaef
define notification categories that we handle
Sep 26, 2018
b6fd5a8
check if notification should be handled before saving
Sep 26, 2018
d5c1918
add completions
Sep 26, 2018
2f7ec5b
remove html tags when saving notification message
Sep 26, 2018
9978640
update params
Sep 26, 2018
8adfb3c
call mark as read
Sep 26, 2018
6a1c9aa
Merge branch 'develop' into feature/T203167
Sep 26, 2018
16ca252
start/stop polling notfications
Sep 26, 2018
9b145b9
handle wikidata errors
Sep 26, 2018
185628c
modify time interval
Sep 26, 2018
5072e3d
start polling when user makes their first edit
Sep 26, 2018
d019a37
check if user is logged in for now
Sep 26, 2018
54e9253
set polling deadline
Sep 26, 2018
2c344b4
add assertions
Sep 26, 2018
723de6c
modify time interval, consolidate assertions
Sep 26, 2018
77f0bbf
validate age
Sep 26, 2018
6b03844
move start
Sep 26, 2018
b7d3ed0
consolidate assertions
Sep 26, 2018
69b2c55
stop controller if user not logged in
Sep 26, 2018
c4b195c
restart start time
Sep 26, 2018
f1775e5
remove print
Sep 26, 2018
c8669fc
stop controller if store not set up
Sep 26, 2018
705b2b5
true -> false
Sep 26, 2018
39f1318
make CSRFOperation return info re token in completion
Sep 26, 2018
3dd26dd
remove unused isLoggedIn method
Sep 26, 2018
4716668
Merge branch 'develop' into feature/T203167
joewalsh Sep 26, 2018
3869698
Observe remote notifications changes in the AppViewController
Sep 26, 2018
487192d
add a missing completion call
Sep 26, 2018
8167dab
rename Category on RemoteNotification to RemoteNotificationCategory, …
Sep 27, 2018
cd1a3ec
group notifications by category number so that @objc can work with them
Sep 27, 2018
7b9361f
use new name
Sep 27, 2018
bf74deb
pass an array of notifications to mark as read
Sep 27, 2018
587a102
show an alert for edit reverts
Sep 27, 2018
238d6f8
add a button to RMessageView
Sep 27, 2018
04b67ce
use theme colors to set button's title color
Sep 27, 2018
55ec9a6
add a method to show an alert with Read more button
Sep 27, 2018
71c6a7e
add ReadMoreAboutRevertedEditViewController
Sep 28, 2018
5b261d4
localize strings
Sep 28, 2018
dfa5a37
project changes
Sep 28, 2018
e581cba
update constraints
Sep 28, 2018
f69c2af
theme themeable's root vc
Sep 28, 2018
45ab69a
update colors
Sep 28, 2018
0b3eb64
fetch wikidata id when fetching article
Sep 28, 2018
9cbb254
add a func to fetch wikidata id
Sep 28, 2018
882ea28
store wikidata id
Sep 28, 2018
f624230
store page id and agent
Sep 28, 2018
eaa3bb6
delegate button tap
Sep 28, 2018
563c6c3
add a method to fetch article with wikidata id
Sep 28, 2018
3e84048
show alert
Sep 28, 2018
b226574
add method to fetch on the context
Sep 28, 2018
d8612c7
add a TODO
Sep 28, 2018
6defad2
different string for multiple notifications
Sep 28, 2018
5129747
fix typo, mark as read
Sep 28, 2018
02a58ca
make sure we never create notifications without ids
Sep 28, 2018
a04ce41
add a method to mark notifications as read by providing notification ids
Sep 28, 2018
df23a6c
pull out marking notifications as read and saving moc
Sep 28, 2018
9d170bd
use new method
Sep 28, 2018
2d37827
mark notifications as read using ids
Sep 28, 2018
0aa3283
add strings relevants to local edit revert notifications
Sep 28, 2018
b34b335
add a method to remove pending notifications
Sep 28, 2018
6146d2b
add a method to show read more vc
Sep 28, 2018
261cdd1
prepare for showing the alert or scheduling a notifcation
Sep 28, 2018
fa89fe1
keep mark as read block around so that there's no need to pass respon…
Sep 28, 2018
92f145e
schedule notification
Sep 28, 2018
4d91e51
show alert
Sep 28, 2018
fb06726
show edit reverted notifications only when app is in the bg
Sep 28, 2018
bcd1835
respond to notification's actions
Sep 28, 2018
298f7a0
update ReadMoreAboutRevertedEditViewControllerDelegate, keep keys ins…
Sep 28, 2018
b2cdbf2
update strings
Sep 28, 2018
bc80ad4
implement delegate methods
Sep 28, 2018
048abbf
format, remove ;
Sep 28, 2018
d7fd431
add new action
Sep 28, 2018
0a76c3d
localize
Sep 28, 2018
a55cd26
wrap the rest of the op in an else statement
Oct 9, 2018
dbc58bd
fetch wikidata id on a background thread
Oct 9, 2018
03c0bca
single completion
Oct 9, 2018
291b17e
indent
Oct 9, 2018
06b8cbb
Merge branch 'develop' into feature/T203167
Oct 10, 2018
857a0f1
use mobileview to get wikibase_item
Oct 10, 2018
78af38e
Merge branch 'feature/T203167' of https://github.com/wikimedia/wikipe…
Oct 10, 2018
2b3cb30
fix typo
Oct 10, 2018
a47d0d1
update sync interval
Oct 10, 2018
e921e04
use WMFJoinedPropertyParameters
Oct 10, 2018
10ef1fa
Merge branch 'develop' into feature/T203167
Oct 10, 2018
20f1539
update attribute name
Oct 10, 2018
a78b133
add isExcluded
Oct 10, 2018
460bc0c
add isExcluded to CoreDataProperties
Oct 10, 2018
7e41ef8
exclude notifications without article context
Oct 10, 2018
e589c59
Merge branch 'feature/T203167' of https://github.com/wikimedia/wikipe…
Oct 10, 2018
d4b434b
always show native notifications, regardless of app state
Oct 10, 2018
ccee068
move 'Reverted edit' to Common Strings
Oct 10, 2018
0e80eca
update naming, take a single notification
Oct 10, 2018
b474fe8
schedule individual native notifications
Oct 10, 2018
5330aaf
mark a single notification
Oct 10, 2018
e4b8f29
unpack new user info dict and show read more vc
Oct 10, 2018
f17b465
pass articleURL to ReadMoreAboutRevertedEditViewController
Oct 10, 2018
40fe32c
remove any notion of multiple notifications from ReadMoreAboutReverte…
Oct 10, 2018
773a047
update ReadMoreAboutRevertedEditViewControllerDelegate
Oct 10, 2018
ff7eddd
update ReadMoreAboutRevertedEditViewControllerDelegate methods, spacing
Oct 10, 2018
9790824
update edit reverted notification keys
Oct 10, 2018
9e48882
localization updates
Oct 10, 2018
8e6b688
remove assertion
Oct 10, 2018
6615b27
remark unseen notifications
Oct 11, 2018
44f0c8f
add a new case to RemoteNotificationsModelChangeType
Oct 11, 2018
b674973
post the model change notifications in a separate method
Oct 11, 2018
3930d4e
notify when objects are updated
Oct 11, 2018
7c9de61
read defaults to false
Oct 11, 2018
96105e6
switch on model change type
Oct 11, 2018
fbe5c7d
add a separate method to schedule notifications
Oct 11, 2018
3af7816
check article stuff first
Oct 11, 2018
81d22ac
Merge branch 'develop' into feature/T203167
Oct 11, 2018
9af52f8
remove test method
Oct 11, 2018
63f5d98
keep response coordinator around
Oct 11, 2018
1924573
mark notifications as seen
Oct 11, 2018
0b63b9c
operate on single notifications
Oct 11, 2018
34e5474
finish if there are no fetched notifications and no saved notificatio…
Oct 11, 2018
e4b1fa8
Merge branch 'develop' into feature/T203167
Oct 11, 2018
87adab6
clone mediawiki auth cookies
Oct 11, 2018
4ca2472
Merge branch 'feature/T203167' of https://github.com/wikimedia/wikipe…
Oct 11, 2018
2f9a09a
stop polling when user makes an anon edit
Oct 11, 2018
231b2f5
restart polling when user logs in
Oct 11, 2018
cc7c7f7
stop & start if syncTimer wasn't nilled out
Oct 11, 2018
d9020a4
add localization updated from merge with develop
Oct 11, 2018
c43ee49
Merge branch 'develop' into feature/T203167
Oct 11, 2018
3c74bd8
add a remote notifications controller toggle
Oct 11, 2018
4c1aff1
Merge branch 'develop' into feature/T203167
Oct 11, 2018
5c54e54
include wikidataID when comparing articles
Oct 12, 2018
361efdd
add wikibase_item to Obama fixture
Oct 12, 2018
beb70d0
return nil if dict nil
Oct 12, 2018
619a315
Merge branch 'bug/wikidata_api_en' into feature/T203167
Oct 12, 2018
aa8d778
Merge branch 'develop' into feature/T203167
Oct 12, 2018
2514a0f
revert changes to library version
Oct 12, 2018
d615da1
Merge branch 'feature/T203167' of https://github.com/wikimedia/wikipe…
Oct 12, 2018
4f2b235
remove wikidataFetcher from article fetcher
Oct 12, 2018
43c0254
remove unused removePendingNotificationRequestsWithIdentifiers
Oct 12, 2018
4b2b076
Merge branch 'develop' into feature/T203167
joewalsh Oct 12, 2018
84a9d32
Merge branch 'feature/T203167' of github-joewalsh:wikimedia/wikipedia…
joewalsh Oct 12, 2018
813d4c5
use notification extension & separate obj-c class, fix release build …
joewalsh Oct 12, 2018
1f93778
remove extension for notification names, use static let
joewalsh Oct 12, 2018
bab1edd
save wmf key value
Oct 12, 2018
caa243d
check if logged in & made authorized wikidata edit before polling
Oct 12, 2018
d4eabe4
move time management to a separate class
Oct 12, 2018
3eae88a
don't start on main
Oct 12, 2018
f504d6f
pass context
Oct 12, 2018
4abbc0a
use an OptionSet in lieu of bools
Oct 12, 2018
ac72028
use enum for state
Oct 12, 2018
00e9f98
don't fetch token for get requests
Oct 12, 2018
13418a7
send viewContext not private context
Oct 12, 2018
0fb081c
don't set bodyEncoding
Oct 12, 2018
a6760e6
store time on remote notifications context
Oct 12, 2018
c0c87a5
inject session
Oct 12, 2018
7479cf6
don't force unwrap
Oct 12, 2018
231fc30
use state in predicates
Oct 12, 2018
45314b3
use dispatchsource
joewalsh Oct 12, 2018
25ee41f
add index
Oct 12, 2018
02a57c1
create a wrapper around DispatchSourceTimer
Oct 13, 2018
deca4e2
use WMFDispatchSourceTimer when scheduling remote notifications opera…
Oct 13, 2018
e5025c5
add a deadlineController
Oct 13, 2018
2433edc
Merge branch 'develop' into feature/T203167
Oct 13, 2018
422a682
remove duplicate prop declaration
Oct 15, 2018
97158b3
make model controller's init failable
Oct 15, 2018
34f2ba0
lock if model controller fails to init
Oct 15, 2018
424b93b
use preferred language codes
Oct 15, 2018
6df1720
fix typo
Oct 15, 2018
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
class RemoteNotificationsOperationsController {
private let apiController: RemoteNotificationsAPIController
private let modelController: RemoteNotificationsModelController
weak var viewContext: NSManagedObjectContext?
private let timeController: RemoteNotificationsOperationsTimeController

private let syncTimeInterval: TimeInterval = 15
private var syncTimer: Timer?
private let operationQueue: OperationQueue
private var isLocked: Bool = false {
didSet {
Expand All @@ -15,10 +13,9 @@ class RemoteNotificationsOperationsController {
}

required init(with viewContext: NSManagedObjectContext) {
self.viewContext = viewContext

apiController = RemoteNotificationsAPIController()
modelController = RemoteNotificationsModelController()
timeController = RemoteNotificationsOperationsTimeController(with: modelController.managedObjectContext)

operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
Expand All @@ -31,55 +28,20 @@ class RemoteNotificationsOperationsController {
NotificationCenter.default.removeObserver(self)
}

let startTimeKey = "WMFRemoteNotificationsOperationsStartTime"
let deadline: TimeInterval = 86400 // 24 hours
private var now: CFAbsoluteTime {
return CFAbsoluteTimeGetCurrent()
}

private var startTime: CFAbsoluteTime? {
set {
assertMainThreadAndViewContext()
if let newValue = newValue {
viewContext?.wmf_setValue(NSNumber(value: newValue), forKey: startTimeKey)
} else {
viewContext?.wmf_setValue(nil, forKey: startTimeKey)
}
}
get {
assertMainThreadAndViewContext()
let keyValue = viewContext?.wmf_keyValue(forKey: startTimeKey)
guard let value = keyValue?.value else {
return nil
}
guard let number = value as? NSNumber else {
assertionFailure("Expected keyValue \(startTimeKey) to be of type NSNumber")
return nil
}
return number.doubleValue
}
}

private func assertMainThreadAndViewContext() {
assert(Thread.isMainThread)
assert(viewContext != nil)
}

public func start() {
guard !isLocked else {
return
}
guard syncTimer == nil else {
guard timeController.wasSyncTimerInvalidated else {
stop()
start()
return
}
syncTimer = Timer.scheduledTimer(timeInterval: syncTimeInterval, target: self, selector: #selector(sync), userInfo: nil, repeats: true)
timeController.setSyncTimer(target: self, selector: #selector(sync))
}

public func stop() {
syncTimer?.invalidate()
syncTimer = nil
timeController.invalidateSyncTimer()
operationQueue.cancelAllOperations()
}

Expand All @@ -92,13 +54,10 @@ class RemoteNotificationsOperationsController {
stop()
return
}
if let startTime = startTime {
guard now - startTime < deadline else {
return
}
} else {
startTime = now
guard timeController.validateTime() else {
return
}

let markAsReadOperation = RemoteNotificationsMarkAsReadOperation(with: apiController, modelController: modelController)
let fetchOperation = RemoteNotificationsFetchOperation(with: apiController, modelController: modelController)
fetchOperation.addDependency(markAsReadOperation)
Expand All @@ -110,7 +69,7 @@ class RemoteNotificationsOperationsController {
// MARK: Notifications

@objc private func didMakeAuthorizedWikidataDescriptionEdit(_ note: Notification) {
startTime = now
timeController.resetStartTime()
}

@objc private func modelControllerDidLoadPersistentStores(_ note: Notification) {
Expand All @@ -122,3 +81,91 @@ class RemoteNotificationsOperationsController {
}
}
}

final class RemoteNotificationsOperationsTimeController {
weak var viewContext: NSManagedObjectContext?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it better to keep this value on the viewContext or add WMFKeyValue to RemoteNotifications.xcdatamodeld (like in EventLogging.xcdatamodeld) and use the private context created by RemoteNotificationsModelController?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be better to use the private context


private let syncTimeInterval: TimeInterval = 15
private var syncTimer: Timer?

let startTimeKey = "WMFRemoteNotificationsOperationsStartTime"
let deadline: TimeInterval = 86400 // 24 hours
private var now: CFAbsoluteTime {
return CFAbsoluteTimeGetCurrent()
}

init(with viewContext: NSManagedObjectContext) {
self.viewContext = viewContext
}

private func assertMainThreadAndViewContext() {
assert(Thread.isMainThread)
assert(viewContext != nil)
}

public func setSyncTimer(target: Any, selector: Selector) {
syncTimer = Timer.scheduledTimer(timeInterval: syncTimeInterval, target: target, selector: selector, userInfo: nil, repeats: true)
}

public var wasSyncTimerInvalidated: Bool {
return syncTimer == nil
}

public func invalidateSyncTimer() {
syncTimer?.invalidate()
syncTimer = nil
}

private func save() {
guard let viewContext = viewContext else {
return
}
guard viewContext.hasChanges else {
return
}
do {
try viewContext.save()
} catch let error {
DDLogError("Error saving managedObjectContext: \(error)")
}
}

public func validateTime() -> Bool {
if let startTime = startTime {
guard now - startTime < deadline else {
return false
}
} else {
startTime = now
}
return true
}

public func resetStartTime() {
startTime = now
}

private var startTime: CFAbsoluteTime? {
set {
assertMainThreadAndViewContext()
if let newValue = newValue {
viewContext?.wmf_setValue(NSNumber(value: newValue), forKey: self.startTimeKey)
} else {
viewContext?.wmf_setValue(nil, forKey: self.startTimeKey)
}
self.save()
}
get {
assertMainThreadAndViewContext()
let keyValue = viewContext?.wmf_keyValue(forKey: startTimeKey)
guard let value = keyValue?.value else {
return nil
}
guard let number = value as? NSNumber else {
assertionFailure("Expected keyValue \(startTimeKey) to be of type NSNumber")
return nil
}
return number.doubleValue
}
}
}