瀏覽代碼

Merge pull request #1757 from nextcloud/fix/activities

Fix Activities view
Marino Faggiana 3 年之前
父節點
當前提交
5b9f37e13f
共有 4 個文件被更改,包括 157 次插入102 次删除
  1. 111 76
      iOSClient/Activity/NCActivity.swift
  2. 11 2
      iOSClient/Data/NCDatabase.swift
  3. 34 23
      iOSClient/Data/NCManageDatabase.swift
  4. 1 1
      iOSClient/NCGlobal.swift

+ 111 - 76
iOSClient/Activity/NCActivity.swift

@@ -25,7 +25,6 @@
 import UIKit
 import SwiftRichString
 import NCCommunication
-import RealmSwift
 
 class NCActivity: UIViewController {
 
@@ -133,7 +132,7 @@ class NCActivity: UIViewController {
 
     @objc func initialize() {
         loadDataSource()
-        loadActivity(idActivity: 0)
+        fetchAll(isInitial: true)
     }
     
     @objc func changeTheming() {
@@ -150,7 +149,7 @@ class NCActivity: UIViewController {
         NCCommunication.shared.putComments(fileId: metadata.fileId, message: message) { (account, errorCode, errorDescription) in
             if errorCode == 0 {
                 self.newCommentField.text = ""
-                self.loadDataSource()
+                self.loadComments()
             } else {
                 NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
             }
@@ -351,12 +350,11 @@ extension NCActivity: UITableViewDataSource {
 
 extension NCActivity: UIScrollViewDelegate {
     func scrollViewDidScroll(_ scrollView: UIScrollView) {
-        if scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.height < 100 {
-            // comments are always loaded in full, only partially load more acitvities
-            if let activity = allItems.compactMap({ $0 as? tableActivity }).last {
-                loadActivity(idActivity: activity.objectId)
-            }
-        }
+        guard
+            scrollView.contentOffset.y > 50,
+            scrollView.contentSize.height - scrollView.frame.height - scrollView.contentOffset.y < -50
+        else { return }
+        fetchAll(isInitial: false)
     }
 }
 
@@ -364,76 +362,120 @@ extension NCActivity: UIScrollViewDelegate {
 
 extension NCActivity {
 
-    func loadDataSource() {
+    func fetchAll(isInitial: Bool) {
+        guard canFetchActivity else { return }
+        self.canFetchActivity = false
 
-        NCUtility.shared.startActivityIndicator(backgroundView: tableView, blurEffect: false)
-        let reloadDispatch = DispatchGroup()
-        self.allItems = []
-        reloadDispatch.enter()
-        if showComments, let metadata = metadata {
-            reloadDispatch.enter()
-            
-            NCCommunication.shared.getComments(fileId: metadata.fileId) { (account, comments, errorCode, errorDescription) in
-                if errorCode == 0 && comments != nil {
-                    NCManageDatabase.shared.addComments(comments!, account: metadata.account, objectId: metadata.fileId)
-                    self.allItems += NCManageDatabase.shared.getComments(account: metadata.account, objectId: metadata.fileId)
-                    reloadDispatch.leave()
-                } else {
-                    if errorCode != NCGlobal.shared.errorResourceNotFound {
-                        NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
-                    }
-                }
+        let height = self.tabBarController?.tabBar.frame.size.height ?? 0
+        NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray)
+
+        let dispatchGroup = DispatchGroup()
+        loadComments(disptachGroup: dispatchGroup)
+        if !isInitial, let activity = allItems.compactMap({ $0 as? tableActivity }).last {
+            loadActivity(idActivity: activity.idActivity, disptachGroup: dispatchGroup)
+        } else {
+            checkRecentActivity(disptachGroup: dispatchGroup)
+        }
+
+        dispatchGroup.notify(queue: .main) {
+            self.loadDataSource()
+            NCUtility.shared.stopActivityIndicator()
+
+            // otherwise is triggered again
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+                self.canFetchActivity = true
             }
         }
-        
+    }
+    
+    func loadDataSource() {
+
+        var newItems = [DateCompareable]()
+        if showComments, let metadata = metadata, let account = NCManageDatabase.shared.getActiveAccount() {
+            let comments = NCManageDatabase.shared.getComments(account: account.account, objectId: metadata.fileId)
+            newItems += comments
+        }
+
         let activities = NCManageDatabase.shared.getActivity(
             predicate: NSPredicate(format: "account == %@", appDelegate.account),
             filterFileId: metadata?.fileId)
-        self.allItems += activities.filter
-        reloadDispatch.leave()
-        
-        reloadDispatch.notify(qos: .userInitiated, flags: .enforceQoS, queue: .main) {
-            self.allItems.sort(by: { $0.dateKey > $1.dateKey })
-            self.sectionDates = self.allItems.reduce(into: Set<Date>()) { partialResult, next in
-                   let newDay = Calendar.current.startOfDay(for: next.dateKey)
-                   partialResult.insert(newDay)
-            }.sorted(by: >)
-            self.tableView.reloadData()
-            NCUtility.shared.stopActivityIndicator()
-        }
+        newItems += activities.filter
+
+        self.allItems = newItems.sorted(by: { $0.dateKey > $1.dateKey })
+        self.sectionDates = self.allItems.reduce(into: Set<Date>()) { partialResult, next in
+            let newDay = Calendar.current.startOfDay(for: next.dateKey)
+            partialResult.insert(newDay)
+        }.sorted(by: >)
+        self.tableView.reloadData()
     }
     
-    @objc func loadActivity(idActivity: Int) {
-        
-        if !canFetchActivity { return }
-        canFetchActivity = false
-        
-        if idActivity > 0 {
-            
-            let height = self.tabBarController?.tabBar.frame.size.height ?? 0
-            NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray)
+    func loadComments(disptachGroup: DispatchGroup? = nil) {
+        guard showComments, let metadata = metadata else { return }
+        disptachGroup?.enter()
+
+        NCCommunication.shared.getComments(fileId: metadata.fileId) { (account, comments, errorCode, errorDescription) in
+            if errorCode == 0, let comments = comments {
+                NCManageDatabase.shared.addComments(comments, account: metadata.account, objectId: metadata.fileId)
+            } else if errorCode != NCGlobal.shared.errorResourceNotFound {
+                NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+            }
+
+            if let disptachGroup = disptachGroup {
+                disptachGroup.leave()
+            } else {
+                self.loadDataSource()
+            }
         }
-        
+    }
+
+    /// Check if most recent activivities are loaded, if not trigger reload
+    func checkRecentActivity(disptachGroup: DispatchGroup) {
+        let recentActivityId = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account)
+
+        guard recentActivityId > 0, metadata == nil else {
+            return self.loadActivity(idActivity: 0, disptachGroup: disptachGroup)
+        }
+
+        disptachGroup.enter()
+
+        NCCommunication.shared.getActivity(
+            since: 0,
+            limit: 1,
+            objectId: nil,
+            objectType: objectType,
+            previews: true) { (account, activities, errorCode, errorDescription) in
+                defer { disptachGroup.leave() }
+
+                guard errorCode == 0,
+                      account == self.appDelegate.account,
+                      let activity = activities.first,
+                      activity.idActivity > recentActivityId
+                else { return }
+
+                self.loadActivity(idActivity: 0, limit: activity.idActivity - recentActivityId, disptachGroup: disptachGroup)
+            }
+    }
+
+    func loadActivity(idActivity: Int, limit: Int = 200, disptachGroup: DispatchGroup) {
+        disptachGroup.enter()
+
         NCCommunication.shared.getActivity(
             since: idActivity,
-            limit: 200,
+            limit: min(limit, 200),
             objectId: metadata?.fileId,
             objectType: objectType,
             previews: true) { (account, activities, errorCode, errorDescription) in
-                
-                if errorCode == 0 && account == self.appDelegate.account {
-                    NCManageDatabase.shared.addActivity(activities, account: account)
-                }
-                
-                NCUtility.shared.stopActivityIndicator()
-                
-                if errorCode == NCGlobal.shared.errorNotModified {
-                    self.canFetchActivity = false
-                } else {
-                    self.canFetchActivity = true
+                defer { disptachGroup.leave() }
+                guard errorCode == 0,
+                      account == self.appDelegate.account,
+                      !activities.isEmpty
+                else { return }
+                NCManageDatabase.shared.addActivity(activities, account: account)
+
+                // update most recently loaded activity only when all activities are loaded (not filtered)
+                if self.metadata == nil {
+                    NCManageDatabase.shared.updateLatestActivityId(activities, account: account)
                 }
-                
-                self.loadDataSource()
             }
     }
 }
@@ -442,9 +484,9 @@ extension NCActivity: NCShareCommentsCellDelegate {
     func tapMenu(with tableComments: tableComments?, sender: Any) {
         toggleMenu(with: tableComments)
     }
-    
+
     func toggleMenu(with tableComments: tableComments?) {
-        
+
         let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions = [NCMenuAction]()
 
@@ -467,7 +509,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
 
                         NCCommunication.shared.updateComments(fileId: metadata.fileId, messageId: tableComments.messageId, message: message) { (account, errorCode, errorDescription) in
                             if errorCode == 0 {
-                                self.loadDataSource()
+                                self.loadComments()
                             } else {
                                 NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
                             }
@@ -488,7 +530,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
 
                     NCCommunication.shared.deleteComments(fileId: metadata.fileId, messageId: tableComments.messageId) { (account, errorCode, errorDescription) in
                         if errorCode == 0 {
-                            self.loadDataSource()
+                            self.loadComments()
                         } else {
                             NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
                         }
@@ -496,14 +538,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
                 }
             )
         )
-        
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_cancel_", comment: ""),
-                icon: UIImage(named: "cancel")!.image(color: NCBrandColor.shared.gray, size: 50),
-                action: nil)
-        )
-        
+
         menuViewController.actions = actions
 
         let menuPanelController = NCMenuPanelController()

+ 11 - 2
iOSClient/Data/NCDatabase.swift

@@ -6,6 +6,7 @@
 //  Copyright © 2017 Marino Faggiana. All rights reserved.
 //
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -130,12 +131,20 @@ class tableActivity: Object, DateCompareable {
     @objc dynamic var note = ""
     @objc dynamic var selector = ""
     @objc dynamic var verbose: Bool = false
-    
+
     override static func primaryKey() -> String {
         return "idPrimaryKey"
     }
 }
 
+class tableActivityLatestId: Object {
+    @objc dynamic var account = ""
+    @objc dynamic var mostRecentlyLoadedActivityId: Int = 0
+    override static func primaryKey() -> String {
+        return "account"
+    }
+}
+
 class tableActivityPreview: Object {
     
     @objc dynamic var account = ""
@@ -148,7 +157,7 @@ class tableActivityPreview: Object {
     @objc dynamic var fileId: Int = 0
     @objc dynamic var view = ""
     @objc dynamic var isMimeTypeIcon: Bool = false
-    
+
     override static func primaryKey() -> String {
         return "idPrimaryKey"
     }

+ 34 - 23
iOSClient/Data/NCManageDatabase.swift

@@ -6,6 +6,7 @@
 //  Copyright © 2017 Marino Faggiana. All rights reserved.
 //
 //  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Author Henrik Storch <henrik.storch@nextcloud.com>
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -225,6 +226,7 @@ class NCManageDatabase: NSObject {
     @objc func clearDatabase(account: String?, removeAccount: Bool) {
         
         self.clearTable(tableActivity.self, account: account)
+        self.clearTable(tableActivityLatestId.self, account: account)
         self.clearTable(tableActivityPreview.self, account: account)
         self.clearTable(tableActivitySubjectRich.self, account: account)
         self.clearTable(tableAvatar.self)
@@ -767,7 +769,7 @@ class NCManageDatabase: NSObject {
     //MARK: Table Activity
 
     @objc func addActivity(_ activities: [NCCommunicationActivity], account: String) {
-    
+
         let realm = try! Realm()
 
         do {
@@ -865,22 +867,10 @@ class NCManageDatabase: NSObject {
         guard let filterFileId = filterFileId else {
             return (all: allActivity, filter: allActivity)
         }
-        // comments are loaded seperately
+
+        // comments are loaded seperately, see NCManageDatabase.getComments
         let filtered = allActivity.filter({ String($0.objectId) == filterFileId && $0.type != "comments" })
         return (all: allActivity, filter: filtered)
-        
-        //HELP: What is this? What is it used for? Why not just use `.filter()` on the array
-        var resultsFilter: [tableActivity] = []
-        for result in results {
-            let resultsActivitySubjectRich = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d", result.account, result.idActivity)
-            for resultActivitySubjectRich in resultsActivitySubjectRich {
-                if filterFileId.contains(resultActivitySubjectRich.id) && resultActivitySubjectRich.key == "file" {
-                    resultsFilter.append(result)
-                    break
-                }
-            }
-        }
-        return(all: allActivity, filter: Array(resultsFilter.map(tableActivity.init)))
     }
     
     @objc func getActivitySubjectRich(account: String, idActivity: Int, key: String) -> tableActivitySubjectRich? {
@@ -915,16 +905,37 @@ class NCManageDatabase: NSObject {
         
         return results
     }
-    
-    @objc func getActivityLastIdActivity(account: String) -> Int {
-        
+
+    @objc func updateLatestActivityId(_ activities: [NCCommunicationActivity], account: String) {
         let realm = try! Realm()
-        
-        if let entities = realm.objects(tableActivity.self).filter("account == %@", account).max(by: { $0.idActivity < $1.idActivity }) {
-            return entities.idActivity
+        let previousRecentId = getLatestActivityId(account: account)
+
+        do {
+            try realm.write {
+                guard
+                    let mostRecentActivityId = activities.map({ $0.idActivity }).max(),
+                    mostRecentActivityId > previousRecentId
+                else { return }
+
+                let newRecentActivity = tableActivityLatestId()
+                newRecentActivity.mostRecentlyLoadedActivityId = mostRecentActivityId
+                newRecentActivity.account = account
+                realm.add(newRecentActivity, update: .all)
+            }
+        } catch {
+            NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
         }
-        
-        return 0
+    }
+
+    @objc func getLatestActivityId(account: String) -> Int {
+
+        let realm = try! Realm()
+        guard let maxId = realm.objects(tableActivityLatestId.self)
+                .filter("account == %@", account)
+                .map({ $0.mostRecentlyLoadedActivityId }).max()
+        else { return 0 }
+
+        return maxId
     }
     
     //MARK: -

+ 1 - 1
iOSClient/NCGlobal.swift

@@ -111,7 +111,7 @@ class NCGlobal: NSObject {
     // Database Realm
     //
     let databaseDefault                             = "nextcloud.realm"
-    let databaseSchemaVersion: UInt64               = 212
+    let databaseSchemaVersion: UInt64               = 213
     
     // Intro selector
     //