N|$vpp?%N@5l57ToCjV4
zQ3r5_5}Quz%Rp;f&OtfGqOJ$}#}wPO3*An8pBXa-<>J$jW|3Ee*MaTciSCf;x@I+)
znI^}7VlImw10DrifoXbn{1g~l=VAxUvt)$O^BR5FsVnX^ytCR~r!dXj2^IJUOCpgG
TJQa3{00000NkvXXu0mjfv1qa|
literal 0
HcmV?d00001
diff --git a/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@4x.png b/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@4x.png
new file mode 100644
index 0000000000000000000000000000000000000000..682122c3f21e9c196b2df4c47179f97e87b9788b
GIT binary patch
literal 2624
zcmV-G3cvMPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91V4wp41ONa40RR91U;qFB0I4%yP5=N2s!2paRCodHok^?|RTPGKf+#3Y
zFc_SIKtPQX4iU%bMx#d5#5e#NbYWt2;l@Oa!Hq*q+!&2R^3~5tNQk{Px7a_Pn~Wa^m(Y+^X4A0R
zN*X}14}j(wpt0a%!cwo?+d3ljH-yih(3EYVqXuaJ1wR5}>7hhLSMi62CW}99sh@)n
z6D(jUIUk&t;AECn>{;9K`w2*!qLcetzLyg322gAzcM3?iZWVrfDHis@8(Fm&sNgF0
zM|sC;`03oD^GVtioo5aPRmf_b>Ku|bWf}amkvNNU14!(CysGF^KvfC-h;J1--NHZf
zO^C5gl58-OQKox{8go>WQMfPKU_J&5;eD@*Tb~AmzHHkK!N$YMhmc<5Lb|LCO*sZ1
zX+yNsVQas#ejCS=i&Jf#jN};7WgD;wH|gO-Teyy)f5~^dh17cCphIce6dkq>D#I&=
z2K+<<>7hhRJtEjyCaJo{(vTs<(j$+wAzB{k6Tj{T=y37{NDICI0)0M&W4EO#gBQ90Iza|H`Xn8#|W3)
zdS){XTn6+=JblTdmnX}>Ti|)F!J~M_1eb7Pyxp_hgoKN_&l6$x19ySnL7vD)wzcft
zHZq}Wu=i=GDOm78Ee?s<3*Iut&e3U%_Lxe=#vypn+5$B&C+1*Kr1&XajjiJKWIxL^
zQ0ubaAk>G}0O|Ko{!M;}F)NKzVmZs?JCx4G%hi9h593H|6iH}mNZGkUngcX}6Z0Rg
zMe(sy15EY<;bGjhBv!Wgs*QZeSCmrAl|sSHFE2RGG%TbqlmEiHm>ZxsPN5{EuTY%!
z`iZ##hX>3wq_2RqGTvzhjZ(~+^96!~`;fjI!nvEoya%jzPb1edq_5myJ_a#2;5(m0
za%}r@*0*hS%ng_yuqy8d^cB=q%nf)mV5aW`^cB=qv<=`+@>4R^%;U168F`w$9Hm6t
zfTUUv1*qD?0erOIMcIc=MKd7o3%1w}dcB~jRBRlA2dyno3v=R*24CCaXXw|k?x+@b
z!N1z#R}pBFCD^dw!CD*=_h_)v65pi$0p-LNH^ILWW0)y8y3MY^e9i%y-*1wGOv;ct
zPNF~H?{x5tgWnghnar4#4z^`YY`NQNRXiSDU2W<1*y3LON5bJG@F@5c`~fzArQmUJ
zn!m^5BZxN^m}E_Hua=QS`P>uJ~5;tOvV6
zqkP5}{rQ?qkqubZIFQfsC7e?HKaOrB&mfx)KY
zn|v-tsXwPsSQy^}l)Fhv`8JXwiY7^IG(|%<$vDVC$=D0QcHk<;CNNmV*E&~GE`={!
z89T`mXe;{t9a8rr_H3384;-|j9=Y#&xh_^6qX6mG6
zslP+yEI#J|Iki0=)@@)&~-Eih2`9`i_Ch1Ypu9w>4@ax~*+gCb(fc}WpxPGd;+
zik5DtUv(_>nuCeG?JhF)>9W+G;B(k9`PALC*YhmwbvU*i|F3Z>W)UNT4hNfoNiOj5
zi38g%Z1P>TPQN*D7RX#+A*6Fzud^czy$(S<;L=mRcP#;?Tl6+{;S-?EVF~?No?~sf
zen5@9ejMV}fQ0007P8anzR#{~yN0&7uT<^KCCSm&fH!@)<@-;21~ptq@<
zleRessQ2Nb98G^4V>kMj19GyvVk>wBm}G%{)WBFn(hE$9qkFry3<-9Lg@n{zC*#^k
z2MDCxGV;DYKnOP%kkD=TDhru8ZeBXiihSo-a@Nh{0kvjCz^tDSO{tYk4Z+BFilz8c
zKTrdO<`4w!1-6@!XeUB7XW{;hTo(e9XtILt#_JaX&o(hibTjH_R82(knURs_Bh^H8
z@CvD{y+9ATMmV^M+C(`3$!cQ-ANlsE%%w@F;*I9R@DV~Sp{
z8$0HFAg&2#7Dky{pr_Rif#wn57bD$$O5MZ^sg^}~@8Sq-H(bg@?+^4MCq9ALm13A7
zPsYc(nGxKd%;2w8H#U!m>>C81$VuRy$SC*If%iQ*+@I?=j`afxXv+5Tj6`;`F1oEq
zoPfC5L{nLO-gmUGMG!p}{AtSs{Vs54`xrJy7MFw*3#0U*9n(B2@^28qP*#mo{F-}dgeE~;VTkjU*>nI`LWeO=w0nj{s)0E(3*ntF42nCv6*
zLP9ryZCP0->t13R{Ddc;K)&-t7Y_sTwJM2S2y8ELSk*7T^66Gk`p#KC5?}I}Cz`?@
z(|o1yyAKH-55CH)M6zCXR~BWVvA{Lf5^Tp8jnz@(NK{iCv!1iFx=C~sbREc@e=3{A
zR%2Y{F${-xR1rgvCFyA(>xQkhfVv^lP1Aq=1etWnI@!32uJL^)lZ8k-E*})&81O3a
zk)0U73v{0$=(BV_@;b)0AKArKq&nD84E;-}6+RU#Xsf>=aaA2TbzBSzqt}~yBI$Ej
z!rVzVV)Y;x-f@Mgn=nNh3Dk&npsc9y4}-(%&ZFbwQM3br?xI%#Us=n5`#`twWw!*1
zs7p)zJnC8SH?WBwbI%33WNq71*0T9VJ)Xkq9zq3v1#AU+Xf_|*0seD&8g(+APL6{2
if>E8GN0)yaEAS7aJVK+!9r$4Y0000P$
literal 0
HcmV?d00001
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 6840597719..2b9dfcd6f1 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -224,6 +224,14 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let dropInteraction = UIDropInteraction(delegate: self)
self.navigationController?.navigationItem.leftBarButtonItems?.first?.customView?.addInteraction(dropInteraction)
+
+ if(!UserDefaults.standard.bool(forKey: "isInitialPrivacySettingsShowed") || isApplicationUpdated()){
+ redirectToPrivacyViewController()
+
+ //set current app version
+ let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String
+ UserDefaults.standard.set(appVersion, forKey: "CurrentAppVersion")
+ }
registerForTraitChanges([UITraitUserInterfaceStyle.self]) { [weak self] (view: NCCollectionViewCommon, _) in
guard let self else { return }
@@ -313,6 +321,19 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
removeImageCache(metadatas: self.dataSource.getMetadatas())
}
+
+ func isApplicationUpdated() -> Bool{
+ let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? ""
+ let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion")
+ return currentVersion != appVersion
+ }
+
+ func redirectToPrivacyViewController(){
+ let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController
+ newViewController.modalPresentationStyle = .fullScreen
+ self.present(newViewController, animated: true, completion: nil)
+ }
func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
let viewController = presentationController.presentedViewController
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.swift b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
new file mode 100644
index 0000000000..e24216220f
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
@@ -0,0 +1,34 @@
+//
+// AnalysisDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+class AnalysisDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var analysisDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ analysisDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ self.rowDescriptor.value = mySwitch.isOn
+ }
+}
+
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.xib b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
new file mode 100644
index 0000000000..45530193f6
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/InitialPrivacySettingsViewController.swift b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
new file mode 100644
index 0000000000..2cd30a4242
--- /dev/null
+++ b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
@@ -0,0 +1,170 @@
+//
+// InitialPrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+import UIKit
+
+class InitialPrivacySettingsViewController: UIViewController {
+
+ @IBOutlet weak var dataPrivacyImage: UIImageView!
+ @IBOutlet weak var acceptButton: UIButton!
+ @IBOutlet weak var privacySettingsHelpText: UITextView!
+ @IBOutlet weak var privacySettingsTitle: UILabel!
+ @IBOutlet weak var widthPriavacyHelpView: NSLayoutConstraint!
+ var privacyHelpText = ""
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ privacySettingsTitle.text = NSLocalizedString("_privacy_settings_title_", comment: "")
+ privacyHelpText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ privacySettingsHelpText.text = privacyHelpText
+ dataPrivacyImage.image = UIImage(named: "dataPrivacy")!.image(color: NCBrandColor.shared.brand, size: 60)
+ privacySettingsHelpText.delegate = self
+ privacySettingsHelpText.textColor = .label
+ privacySettingsHelpText.hyperLink(originalText: privacyHelpText,
+ linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): LinkType.privacyPolicy.rawValue,
+ NSLocalizedString("_key_reject_help_", comment: ""): LinkType.reject.rawValue,
+ NSLocalizedString("_key_settings_help_", comment: ""): LinkType.settings.rawValue])
+
+ acceptButton.backgroundColor = NCBrandColor.shared.brand
+ acceptButton.tintColor = UIColor.white
+ acceptButton.layer.cornerRadius = 5
+ acceptButton.layer.borderWidth = 1
+ acceptButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+ acceptButton.setTitle(NSLocalizedString("_accept_button_title_", comment: ""), for: .normal)
+ privacySettingsHelpText.centerText()
+ privacySettingsHelpText.font = UIFont(name: privacySettingsHelpText.font!.fontName, size: 16)
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ self.navigationController?.navigationBar.isHidden = true
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ self.navigationController?.navigationBar.isHidden = false
+ }
+
+ override func viewDidLayoutSubviews(){
+ if UIDevice.current.userInterfaceIdiom == .pad {
+ widthPriavacyHelpView.constant = UIScreen.main.bounds.width - 100
+ }
+ }
+
+ @IBAction func onAcceptButtonClicked(_ sender: Any) {
+ requestPermission()
+ }
+
+ //NEWLY ADDED PERMISSIONS FOR iOS 14
+ func requestPermission() {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization { status in
+ switch status {
+ case .authorized:
+ // Tracking authorization dialog was shown
+ // and we are authorized
+ print("Authorized")
+ // Now that we are authorized we can get the IDFA
+ print(ASIdentifierManager.shared().advertisingIdentifier)
+ case .denied:
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ print("Denied")
+ case .notDetermined:
+ // Tracking authorization dialog has not been shown
+ print("Not Determined")
+ case .restricted:
+ print("Restricted")
+ @unknown default:
+ print("Unknown")
+ }
+ }
+ } else {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+ self.dismiss(animated: true, completion: nil)
+ }
+}
+// MARK: - UITextViewDelegate
+extension InitialPrivacySettingsViewController: UITextViewDelegate {
+ func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
+ if let linkType = LinkType(rawValue: URL.absoluteString) {
+ // TODO: handle linktype here with switch or similar.
+ switch linkType {
+ case LinkType.privacyPolicy:
+ //let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let privacyViewController = PrivacyPolicyViewController()
+ self.navigationController?.pushViewController(privacyViewController, animated: true)
+ case LinkType.reject:
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ self.dismiss(animated: true, completion: nil)
+ case LinkType.settings:
+ let privacySettingsViewController = PrivacySettingsViewController()
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ self.navigationController?.pushViewController(privacySettingsViewController, animated: true)
+ }
+ print("handle link:: \(linkType)")
+ }
+ return false
+ }
+}
+
+public extension UITextView {
+
+ func hyperLink(originalText: String, linkTextsAndTypes: [String: String]) {
+
+ let style = NSMutableParagraphStyle()
+ style.alignment = .left
+
+ let attributedOriginalText = NSMutableAttributedString(string: originalText)
+
+ let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.label, range: fullRange)
+ for linkTextAndType in linkTextsAndTypes {
+ let linkRange = attributedOriginalText.mutableString.range(of: linkTextAndType.key)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: linkTextAndType.value, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: NCBrandColor.shared.brand, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
+ }
+
+ self.linkTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.brand]
+ self.attributedText = attributedOriginalText
+ }
+
+ func centerText() {
+ self.textAlignment = .justified
+ let fittingSize = CGSize(width: 300, height: CGFloat.greatestFiniteMagnitude)
+ let size = sizeThatFits(fittingSize)
+ let topOffset = (bounds.size.height - size.height * zoomScale) / 2
+ let positiveTopOffset = max(1, topOffset)
+ contentOffset.y = -positiveTopOffset
+ }
+}
+
+enum LinkType: String {
+ case reject
+ case privacyPolicy
+ case settings
+}
+
+
diff --git a/iOSClient/Settings/NCSettings.storyboard b/iOSClient/Settings/NCSettings.storyboard
new file mode 100644
index 0000000000..19a320515b
--- /dev/null
+++ b/iOSClient/Settings/NCSettings.storyboard
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/PrivacyPolicyViewController.swift b/iOSClient/Settings/PrivacyPolicyViewController.swift
new file mode 100644
index 0000000000..141c444fca
--- /dev/null
+++ b/iOSClient/Settings/PrivacyPolicyViewController.swift
@@ -0,0 +1,62 @@
+//
+// PrivacyPolicyViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+import WebKit
+
+class PrivacyPolicyViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ var myWebView = WKWebView()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.title = NSLocalizedString("_privacy_policy_", comment: "")
+
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://static.magentacloud.de/privacy/datenschutzhinweise_app.htm")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ }
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
new file mode 100644
index 0000000000..3e60dd6212
--- /dev/null
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -0,0 +1,133 @@
+//
+// PrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+
+class PrivacySettingsViewController: XLFormViewController{
+
+ @objc public var isShowSettingsButton: Bool = false
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+
+ let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
+ self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
+ isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ changeTheming()
+ }
+
+ @objc func changeTheming() {
+ tableView.backgroundColor = .systemGroupedBackground
+ tableView.separatorColor = .none
+ tableView.separatorColor = .clear
+ tableView.reloadData()
+ initializeForm()
+ }
+
+ //MARK: XLForm
+ func initializeForm() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
+ form.addFormSection(section)
+
+ //custom cell
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
+
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
+ if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
+ }else {
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
+ }
+
+ section.addFormRow(row)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
+ row.cellConfig["backgroundColor"] = UIColor.clear
+
+ if(isShowSettingsButton){
+ section.addFormRow(row)
+ }
+
+ self.form = form
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "SaveSettingsButton" {
+ print("save settings clicked")
+ //TODO save button state and leave the page
+ self.navigationController?.popViewController(animated: true)
+
+ }
+ if formRow.tag == "AnalysisDataCollectionSwitch"{
+ if (formRow.value! as AnyObject).boolValue {
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
+ if status == .denied {
+ guard let url = URL(string: UIApplication.openSettingsURLString) else {
+ return
+ }
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ })
+ }
+ }
+ UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+ }
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.swift b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
new file mode 100644
index 0000000000..f80aa97e89
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
@@ -0,0 +1,34 @@
+//
+// RequiredDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+
+class RequiredDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var requiredDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ //requiredDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+
+ requiredDataCollectionSwitchControl.isOn = true
+ requiredDataCollectionSwitchControl.isEnabled = false
+ }
+
+ override func update() {
+ super.update()
+ }
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.xib b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
new file mode 100644
index 0000000000..66156c6e06
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.swift b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
new file mode 100644
index 0000000000..6205727723
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
@@ -0,0 +1,46 @@
+//
+// SaveSettingsCustomButtonCell.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+
+class SaveSettingsCustomButtonCell: XLFormButtonCell {
+
+ @IBOutlet weak var saveSettingsButton: UIButton!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ self.separatorInset = UIEdgeInsets(top: 0, left: .greatestFiniteMagnitude, bottom: 0, right: .greatestFiniteMagnitude)
+ saveSettingsButton.setTitle(NSLocalizedString("_save_settings_", comment: ""), for: .normal)
+ saveSettingsButton.addTarget(self, action: #selector(saveButtonClicked), for: .touchUpInside)
+
+ }
+
+ override func configure() {
+ super.configure()
+ saveSettingsButton.backgroundColor = NCBrandColor.shared.brand
+ saveSettingsButton.tintColor = UIColor.white
+ saveSettingsButton.layer.cornerRadius = 5
+ saveSettingsButton.layer.borderWidth = 1
+ saveSettingsButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+
+ }
+
+ override func update() {
+ super.update()
+
+ }
+
+ @objc func saveButtonClicked(sender: UIButton) {
+ self.rowDescriptor.value = sender
+
+ }
+
+}
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.xib b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
new file mode 100644
index 0000000000..0dd197da31
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From df29809c194d390c6c4399ba0241e50e1d82e603 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 18 Oct 2023 14:58:46 +0530
Subject: [PATCH 019/177] NMC 1984 - Unit test added for privacy policy view
controller
---
.../PrivacyPolicyTest.swift | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
create mode 100644 Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
diff --git a/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
new file mode 100644
index 0000000000..f6265f48e7
--- /dev/null
+++ b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
@@ -0,0 +1,154 @@
+//
+// PrivacyPolicyTest.swift
+// NextcloudTests
+//
+// Created by A200073704 on 27/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+import XLForm
+
+
+ class PrivacyPolicyTest: XCTestCase {
+
+ var viewController: InitialPrivacySettingsViewController?
+ var privacySettingsView = PrivacySettingsViewController()
+
+ override func setUpWithError() throws {
+
+ // To Create an instance of UIStoryboard
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+
+ // To Instantiate UIViewController with Storyboard ID
+ viewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController
+
+ // Outlets are connected
+ let _ = viewController?.view
+
+ // Make the viewDidLoad() execute.
+ viewController?.loadViewIfNeeded()
+
+ }
+
+ override func tearDownWithError() throws {
+ viewController = nil
+ }
+
+ func testPrivacyPolicyViewControllerIsOpen() {
+
+ // Check that the InitialPrivacyPolicyViewController gets opened
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ if let privacyPolicyViewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController {
+ let navigationController = UINavigationController(rootViewController: privacyPolicyViewController)
+
+ privacyPolicyViewController.loadViewIfNeeded()
+
+ XCTAssertTrue(navigationController.topViewController is InitialPrivacySettingsViewController, "Privacy policy view controller should be open")
+ }
+ }
+
+ func testTextViewHasCorrectText() {
+
+ //Check that the text displayed is correct
+ let expectedText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ viewController?.privacySettingsHelpText?.text = expectedText
+
+ let actualText = viewController?.privacySettingsHelpText?.text
+ XCTAssertEqual(actualText, expectedText, "The text view does not have the expected text")
+ }
+
+ func testHasAcceptButton() {
+
+ // Check that view has the accept button
+ let acceptButton = viewController?.acceptButton
+
+ XCTAssertNotNil(acceptButton, "View controller does not have an accept button")
+
+ }
+
+ func testSettingsLinkTypeNavigatesToPrivacySettingsViewController() {
+
+ // Simulate tapping the "Settings" link type
+ let linkType = LinkType.settings
+
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_settings_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation stack
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacySettingsViewController)
+ }
+
+ func testPrivacyPolicyLinkType_NavigatesToPrivacyPolicyViewController() {
+
+ // Simulate tapping the "Privacy Policy" link type
+ let linkType = LinkType.privacyPolicy
+
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacyPolicyViewController)
+ }
+
+ func testCorrectImagePresentOnInitialPrivacySettingsViewController() {
+
+ // Check that the image view has the correct image
+ let expectedImage = UIImage(named: "dataPrivacy")
+ XCTAssertNotNil(expectedImage)
+ }
+
+ func testAcceptButtonHasBackgroundColor() {
+
+ // Check that the accept button has the correct background color
+ let expectedColor = NCBrandColor.shared.brand
+ XCTAssertEqual(viewController?.acceptButton.backgroundColor, expectedColor)
+
+ }
+
+ func testShowSaveSettingsButton() {
+
+ privacySettingsView.isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+
+ XCTAssertTrue(privacySettingsView.isShowSettingsButton)
+
+ }
+
+ func testRequiredDataCollectionSectionExists() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // the section with the title "Required Data Collection"
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected 'Required Data Collection' section to exist in form.")
+ }
+
+ func testAnalysisDataCollectionSection() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // row with tag "AnalysisDataCollectionSwitch"
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+
+ // Assert that the row exists
+ XCTAssertNotNil(row, "Expected row with tag 'AnalysisDataCollectionSwitch' to exist in form.")
+
+ // Verify the switch is off
+ XCTAssertFalse(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn"), "Expected isAnalysisDataCollectionSwitchOn to be false.")
+ }
+
+
+}
From f0d51eb64358d3deec6f891edb8a62b5dfd87967 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Mon, 4 Dec 2023 12:01:04 +0530
Subject: [PATCH 020/177] NMC 1984 - privacySettingsViewController file removed
to avoid conflicts
---
.../PrivacySettingsViewController.swift | 133 ------------------
1 file changed, 133 deletions(-)
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
index 3e60dd6212..e69de29bb2 100644
--- a/iOSClient/Settings/PrivacySettingsViewController.swift
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -1,133 +0,0 @@
-//
-// PrivacySettingsViewController.swift
-// Nextcloud
-//
-// Created by A200073704 on 25/04/23.
-// Copyright © 2023 Marino Faggiana. All rights reserved.
-//
-
-import Foundation
-import AppTrackingTransparency
-import AdSupport
-
-class PrivacySettingsViewController: XLFormViewController{
-
- @objc public var isShowSettingsButton: Bool = false
-
- override func viewDidLoad() {
- super.viewDidLoad()
- self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
-
- NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-
- let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
- self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
- isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
- self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
- changeTheming()
- }
-
- @objc func changeTheming() {
- tableView.backgroundColor = .systemGroupedBackground
- tableView.separatorColor = .none
- tableView.separatorColor = .clear
- tableView.reloadData()
- initializeForm()
- }
-
- //MARK: XLForm
- func initializeForm() {
- let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
- form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
-
- var section : XLFormSectionDescriptor
- var row : XLFormRowDescriptor
-
- // Section: Destination Folder
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = " "
- form.addFormSection(section)
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
- form.addFormSection(section)
-
- //custom cell
- section = XLFormSectionDescriptor.formSection(withTitle: "")
- section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
- form.addFormSection(section)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
-
- row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
- row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
- row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
- row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
- row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
- row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
- section.addFormRow(row)
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
- form.addFormSection(section)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
-
- row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
- row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
- row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
- row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
- row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
- row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
- if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
- row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
- }else {
- row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
- }
-
- section.addFormRow(row)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
-
- section = XLFormSectionDescriptor.formSection(withTitle: "")
- form.addFormSection(section)
-
- row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
- row.cellConfig["backgroundColor"] = UIColor.clear
-
- if(isShowSettingsButton){
- section.addFormRow(row)
- }
-
- self.form = form
- }
-
- override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
- super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
-
- if formRow.tag == "SaveSettingsButton" {
- print("save settings clicked")
- //TODO save button state and leave the page
- self.navigationController?.popViewController(animated: true)
-
- }
- if formRow.tag == "AnalysisDataCollectionSwitch"{
- if (formRow.value! as AnyObject).boolValue {
- if #available(iOS 14, *) {
- ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
- if status == .denied {
- guard let url = URL(string: UIApplication.openSettingsURLString) else {
- return
- }
- if UIApplication.shared.canOpenURL(url) {
- UIApplication.shared.open(url, options: [:])
- }
- }
- })
- }
- }
- UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
- }
- }
-}
From 06006322e207ab577848f37bd158d43a6d82dde8 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Tue, 26 Dec 2023 13:29:37 +0530
Subject: [PATCH 021/177] NMC 1984 - Privacy setting view controller added
---
.../PrivacySettingsViewController.swift | 147 ++++++++++++++++++
1 file changed, 147 insertions(+)
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
index e69de29bb2..b7883ad800 100644
--- a/iOSClient/Settings/PrivacySettingsViewController.swift
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -0,0 +1,147 @@
+//
+// PrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+
+class PrivacySettingsViewController: XLFormViewController{
+
+ @objc public var isShowSettingsButton: Bool = false
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+
+ let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
+ self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
+ isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ changeTheming()
+ }
+
+ @objc func changeTheming() {
+ tableView.backgroundColor = .systemGroupedBackground
+ tableView.separatorColor = .none
+ tableView.separatorColor = .clear
+ tableView.reloadData()
+ initializeForm()
+ }
+
+
+
+ //MARK: XLForm
+
+ func initializeForm() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ //custom cell
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
+ if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
+ }else {
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
+ }
+
+ section.addFormRow(row)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ form.addFormSection(section)
+
+
+ row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
+ row.cellConfig["backgroundColor"] = UIColor.clear
+
+ if(isShowSettingsButton){
+ section.addFormRow(row)
+ }
+
+
+ self.form = form
+ }
+
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "SaveSettingsButton" {
+ print("save settings clicked")
+ //TODO save button state and leave the page
+ self.navigationController?.popViewController(animated: true)
+
+ }
+ if formRow.tag == "AnalysisDataCollectionSwitch"{
+ if (formRow.value! as AnyObject).boolValue {
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
+ if status == .denied {
+ guard let url = URL(string: UIApplication.openSettingsURLString) else {
+ return
+ }
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ })
+ }
+ }
+ UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+
+ }
+
+}
From 3750f299e8b18975e993e1b106b57e80d16b4463 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 14:10:39 +0530
Subject: [PATCH 022/177] NMC 1984 - Privacy policy customisation changes
added
---
Nextcloud.xcodeproj/project.pbxproj | 47 ++
...mon+CollectionViewDelegateFlowLayout.swift | 43 ++
.../NCCollectionViewCommon.swift | 652 ++++++++++++++++--
3 files changed, 677 insertions(+), 65 deletions(-)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index 0d081f4036..142bc4fec7 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,19 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B54315362DA64EB100981E7E /* PrivacyPolicyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */; };
+ B54315412DA669C700981E7E /* InitialPrivacySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */; };
+ B54315422DA669C700981E7E /* RequiredDataCollectionSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */; };
+ B54315432DA669C700981E7E /* PrivacySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */; };
+ B54315442DA669C700981E7E /* AnalysisDataCollectionSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */; };
+ B54315452DA669C700981E7E /* PrivacyPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */; };
+ B54315462DA669C700981E7E /* SaveSettingsCustomButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */; };
+ B54315472DA669C700981E7E /* AnalysisDataCollectionSwitch.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */; };
+ B54315482DA669C700981E7E /* NCSettings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B543153A2DA669C700981E7E /* NCSettings.storyboard */; };
+ B54315492DA669C700981E7E /* SaveSettingsCustomButtonCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */; };
+ B543154A2DA669C700981E7E /* RequiredDataCollectionSwitch.xib in Resources */ = {isa = PBXBuildFile; fileRef = B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1367,6 +1380,18 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyTest.swift; sourceTree = ""; };
+ B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalysisDataCollectionSwitch.swift; sourceTree = ""; };
+ B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AnalysisDataCollectionSwitch.xib; sourceTree = ""; };
+ B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialPrivacySettingsViewController.swift; sourceTree = ""; };
+ B543153A2DA669C700981E7E /* NCSettings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCSettings.storyboard; sourceTree = ""; };
+ B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyViewController.swift; sourceTree = ""; };
+ B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacySettingsViewController.swift; sourceTree = ""; };
+ B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequiredDataCollectionSwitch.swift; sourceTree = ""; };
+ B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RequiredDataCollectionSwitch.xib; sourceTree = ""; };
+ B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveSettingsCustomButtonCell.swift; sourceTree = ""; };
+ B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SaveSettingsCustomButtonCell.xib; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2171,6 +2196,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -2646,6 +2673,16 @@
F76882042C0DD1E7001CF441 /* Settings */ = {
isa = PBXGroup;
children = (
+ B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */,
+ B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */,
+ B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */,
+ B543153A2DA669C700981E7E /* NCSettings.storyboard */,
+ B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */,
+ B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */,
+ B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */,
+ B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */,
+ B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */,
+ B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */,
F768820B2C0DD1E7001CF441 /* Settings */,
F76882162C0DD1E7001CF441 /* AutoUpload */,
F389C9F32CEE381E00049762 /* SelectAlbum */,
@@ -4073,6 +4110,10 @@
3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */,
AF93471C27E2361E002537EE /* NCShareHeader.xib in Resources */,
F75D901F2D2BE12E003E740B /* NCRecommendationsCell.xib in Resources */,
+ B54315472DA669C700981E7E /* AnalysisDataCollectionSwitch.xib in Resources */,
+ B54315482DA669C700981E7E /* NCSettings.storyboard in Resources */,
+ B54315492DA669C700981E7E /* SaveSettingsCustomButtonCell.xib in Resources */,
+ B543154A2DA669C700981E7E /* RequiredDataCollectionSwitch.xib in Resources */,
F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */,
F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */,
F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */,
@@ -4719,6 +4760,12 @@
F72408332B8A27C900F128E2 /* NCMedia+Command.swift in Sources */,
F755CB402B8CB13C00CE27E9 /* NCMediaLayout.swift in Sources */,
F73EF7B72B0224AB0087E6E9 /* NCManageDatabase+ExternalSites.swift in Sources */,
+ B54315412DA669C700981E7E /* InitialPrivacySettingsViewController.swift in Sources */,
+ B54315422DA669C700981E7E /* RequiredDataCollectionSwitch.swift in Sources */,
+ B54315432DA669C700981E7E /* PrivacySettingsViewController.swift in Sources */,
+ B54315442DA669C700981E7E /* AnalysisDataCollectionSwitch.swift in Sources */,
+ B54315452DA669C700981E7E /* PrivacyPolicyViewController.swift in Sources */,
+ B54315462DA669C700981E7E /* SaveSettingsCustomButtonCell.swift in Sources */,
AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */,
AFA2AC8527849604008E1EA7 /* NCActivityCommentView.swift in Sources */,
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
index 06795639bc..42bbbd6038 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
@@ -13,4 +13,47 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return sizeForFooterInSection(section: section)
}
+
+ func getHeaderHeight() -> CGFloat {
+
+ var size: CGFloat = 0
+ // transfer in progress
+ if headerMenuTransferView,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(NCNetworking.shared.transferInForegorund?.ocId),
+ metadata.isTransferInForeground {
+ if !isSearchingMode {
+ size += NCGlobal.shared.heightHeaderTransfer
+ }
+ } else {
+ NCNetworking.shared.transferInForegorund = nil
+ }
+
+ if headerMenuButtonsView {
+ size += NCGlobal.shared.heightButtonsView
+ }
+
+ return size
+ }
+
+ func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
+
+ var headerRichWorkspace: CGFloat = 0
+
+ if let richWorkspaceText = richWorkspaceText, showDescription {
+ let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces)
+ if !trimmed.isEmpty && !isSearchingMode {
+ headerRichWorkspace = UIScreen.main.bounds.size.height / 6
+ }
+ }
+
+ if isSearchingMode || layoutForView?.layout == NCGlobal.shared.layoutGrid || dataSource.numberOfSections() > 1 {
+ if section == 0 {
+ return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
+ } else {
+ return (0, 0, NCGlobal.shared.heightSection)
+ }
+ } else {
+ return (getHeaderHeight(), headerRichWorkspace, 0)
+ }
+ }
}
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 2b9dfcd6f1..2a8cb39d2a 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -8,7 +8,7 @@ import RealmSwift
import NextcloudKit
import EasyTipView
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionFirstHeaderDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, NCTransferDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
@@ -56,10 +56,17 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var titleCurrentFolder = ""
var titlePreviusFolder: String?
var enableSearchBar: Bool = false
+ let maxImageGrid: CGFloat = 7
+ var headerMenu: NCSectionHeaderMenu?
+ var headerMenuTransferView = false
+ var headerMenuButtonsView: Bool = true
var headerRichWorkspaceDisable: Bool = false
+
+ var groupByField = "name"
var emptyImageName: String?
var emptyImageColors: [UIColor]?
+ var emptyImage: UIImage?
var emptyTitle: String = ""
var emptyDescription: String = ""
@@ -78,6 +85,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var numberOfColumns: Int = 0
var lastNumberOfColumns: Int = 0
+ var isTransitioning: Bool = false
+ var selectableDataSource: [RealmSwiftObject] { dataSource.getMetadataSourceForAllSections() }
+ var pushed: Bool = false
+ var emptyDataSet: NCEmptyDataSet?
+
let heightHeaderRecommendations: CGFloat = 160
let heightHeaderSection: CGFloat = 30
@@ -162,8 +174,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
view.backgroundColor = .systemBackground
collectionView.backgroundColor = .systemBackground
- refreshControl.tintColor = .clear
-
+ refreshControl.tintColor = .gray
+
+ listLayout = NCListLayout()
+ gridLayout = NCGridLayout()
+
if enableSearchBar {
searchController = UISearchController(searchResultsController: nil)
searchController?.searchResultsUpdater = self
@@ -172,7 +187,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
searchController?.searchBar.delegate = self
searchController?.searchBar.autocapitalizationType = .none
navigationItem.searchController = searchController
- navigationItem.hidesSearchBarWhenScrolling = true
+ navigationItem.hidesSearchBarWhenScrolling = false
+ navigationItem.backBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_back_", comment: ""), style: .plain, target: nil, action: nil)
}
// Cell
@@ -182,12 +198,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView.register(UINib(nibName: "NCTransferCell", bundle: nil), forCellWithReuseIdentifier: "transferCell")
// Header
- collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeader")
- collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeader")
+ collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeader")
- collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionHeader")
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
// Footer
collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
@@ -207,6 +219,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.mainNavigationController?.resetPlusButtonAlpha()
}
}
+
+ // Empty
+ emptyDataSet = NCEmptyDataSet(view: collectionView, offset: getHeaderHeight(), delegate: self)
let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:)))
longPressedGesture.minimumPressDuration = 0.5
@@ -256,6 +271,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
navigationController?.navigationBar.topItem?.title = titlePreviusFolder
}
navigationItem.title = titleCurrentFolder
+ navigationController?.setNavigationBarAppearance()
+ navigationController?.navigationBar.prefersLargeTitles = true
+ navigationController?.setNavigationBarHidden(false, animated: true)
+
+ appDelegate.activeViewController = self
if tabBarSelect == nil {
tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, viewController: self, delegate: self)
@@ -263,12 +283,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
isEditMode = false
- Task {
- await (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems()
- await (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
- }
+ /// Magentacloud branding changes hide user account button on left navigation bar
+// setNavigationLeftItems()
+ setNavigationRightItems()
layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
+ gridLayout.column = CGFloat(layoutForView?.columnGrid ?? 3)
if isLayoutList {
collectionView?.collectionViewLayout = listLayout
self.layoutType = global.layoutList
@@ -295,6 +315,30 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(closeRichWorkspaceWebView), name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(changeStatusFolderE2EE(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeStatusFolderE2EE), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(changeLayout(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeLayout), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(copyMoveFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterCopyMoveFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterRenameFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(createFolder(_:)), name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(favoriteFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadStartFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadCancelFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadStartFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadStartFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadedLivePhoto(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadCancelFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(updateShare(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: global.notificationCenterProgressTask), object: nil)
+
+ // FIXME: iPAD PDF landscape mode iOS 16
+ DispatchQueue.main.async {
+ self.collectionView?.collectionViewLayout.invalidateLayout()
+ }
}
override func viewWillDisappear(_ animated: Bool) {
@@ -302,7 +346,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.networking.cancelUnifiedSearchFiles()
dismissTip()
-
+ pushed = false
+ toggleSelect(isOn: false)
// Cancel Queue & Retrieves Properties
self.networking.downloadThumbnailQueue.cancelAll()
self.networking.unifiedSearchQueue.cancelAll()
@@ -318,17 +363,38 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeStatusFolderE2EE), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeLayout), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterRenameFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
removeImageCache(metadatas: self.dataSource.getMetadatas())
}
- func isApplicationUpdated() -> Bool{
+ func isApplicationUpdated() -> Bool {
let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? ""
let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion")
return currentVersion != appVersion
}
- func redirectToPrivacyViewController(){
+ func redirectToPrivacyViewController() {
let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController
newViewController.modalPresentationStyle = .fullScreen
@@ -604,8 +670,352 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.present(alertController, animated: true)
}
+ self.collectionView.collectionViewLayout.invalidateLayout()
+
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
+ }
+
+ @objc func reloadDataSource(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as? NSDictionary {
+ if let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ if let clearDataSource = userInfo["clearDataSource"] as? Bool, clearDataSource {
+ self.dataSource.removeAll()
+ }
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func getServerData(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ getServerData()
+ }
+
+ @objc func reloadHeader(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ account == session.account
+ else { return }
+
+ self.collectionView.reloadData()
+ }
+
+ @objc func changeStatusFolderE2EE(_ notification: NSNotification) {
+ reloadDataSource()
+ }
+
+ @objc func closeRichWorkspaceWebView() {
+ reloadDataSource()
}
+ @objc func deleteFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ } else {
+ NCContentPresenter().showError(error: error)
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func copyMoveFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ reloadDataSource()
+ }
+ }
+
+ @objc func renameFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let error = userInfo["error"] as? NKError,
+ account == session.account
+ else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ if error != .success {
+ NCContentPresenter().showError(error: error)
+ }
+ reloadDataSource()
+ } else {
+ collectionView.reloadData()
+ }
+ }
+
+ @objc func createFolder(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account,
+ let withPush = userInfo["withPush"] as? Bool,
+ let metadata = database.getMetadataFromOcId(ocId)
+ else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if metadata.serverUrl + "/" + metadata.fileName == self.serverUrl {
+ reloadDataSource()
+ } else if withPush, metadata.serverUrl == self.serverUrl {
+ reloadDataSource()
+ if let sceneIdentifier = userInfo["sceneIdentifier"] as? String {
+ if sceneIdentifier == controller?.sceneIdentifier {
+ pushMetadata(metadata)
+ }
+ } else {
+ pushMetadata(metadata)
+ }
+ }
+ }
+
+ @objc func favoriteFile(_ notification: NSNotification) {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if self is NCFavorite {
+ return reloadDataSource()
+ }
+
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ serverUrl == self.serverUrl
+ else { return }
+
+ reloadDataSource()
+ }
+
+ @objc func downloadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ !isSearchingMode,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)
+ else { return }
+
+ // Header view trasfer
+ if metadata.isTransferInForeground {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: 0)
+ DispatchQueue.main.async { self.collectionView?.reloadData() }
+ }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedLivePhoto(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func updateShare(_ notification: NSNotification) {
+ if isSearchingMode {
+ networkSearch()
+ } else {
+ self.dataSource.removeAll()
+ getServerData()
+ }
+ }
+
+ @objc func triggerProgressTask(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let progressNumber = userInfo["progress"] as? NSNumber,
+ let totalBytes = userInfo["totalBytes"] as? Int64,
+ let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
+ let ocId = userInfo["ocId"] as? String,
+ let ocIdTransfer = userInfo["ocIdTransfer"] as? String,
+ let session = userInfo["session"] as? String
+ else { return }
+
+ let chunk: Int = userInfo["chunk"] as? Int ?? 0
+ let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
+
+ let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected))
+
+ // HEADER
+// if self.headerMenuTransferView, transfer.session.contains("upload") {
+// self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
+// self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
+// }
+
+ DispatchQueue.main.async {
+ if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
+ if NCNetworking.shared.transferInForegorund?.ocId == ocId {
+ NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
+ } else {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: progressNumber.floatValue)
+ self.collectionView.reloadData()
+ }
+ self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue
+ } else {
+ guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath,
+ let cell = self.collectionView?.cellForItem(at: indexPath),
+ let cell = cell as? NCCellProtocol else { return }
+ if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
+ cell.fileProgressView?.isHidden = true
+ cell.fileProgressView?.progress = .zero
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore)
+ if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+ cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+ } else {
+ cell.fileInfoLabel?.text = ""
+ cell.fileSubinfoLabel?.text = ""
+ }
+ } else {
+ cell.fileProgressView?.isHidden = false
+ cell.fileProgressView?.progress = progressNumber.floatValue
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop)
+ let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
+ if status == NCGlobal.shared.metadataStatusDownloading {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else if status == NCGlobal.shared.metadataStatusUploading {
+ if totalBytes > 0 {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ …"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // MARK: - Layout
+
+ func setNavigationLeftItems() {
+ navigationItem.title = titleCurrentFolder
+ }
+
func getNavigationTitle() -> String {
let tblAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
if let tblAccount,
@@ -647,6 +1057,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.navigationItem.title = self.titleCurrentFolder
}
+ // MARK: - Empty
+
+ func emptyDataSetView(_ view: NCEmptyView) {
+
+ self.emptyDataSet?.setOffset(getHeaderHeight())
+ if isSearchingMode {
+ view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ if self.dataSourceTask?.state == .running {
+ view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
+ } else {
+ view.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "")
+ }
+ view.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "")
+ } else if self.dataSourceTask?.state == .running {
+ view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+ view.emptyDescription.text = ""
+ } else {
+ if serverUrl.isEmpty {
+ view.emptyImage.image = emptyImage
+ view.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "")
+ view.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "")
+ } else {
+ view.emptyImage.image = UIImage(named: "folder_nmcloud")
+ view.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "")
+ view.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "")
+ }
+ }
+ }
+
// MARK: - SEARCH
func searchController(enabled: Bool) {
@@ -699,23 +1139,34 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - TAP EVENT
- func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
- tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
- func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
- tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) {
+ if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
-
NCDownloadAction.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
+ TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""])
+ appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue))
+ NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
}
- func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ func tapMoreGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
- toggleMenu(metadata: metadata, image: image, sender: sender)
+// toggleMenu(metadata: metadata, image: image)
+ if namedButtonMore == NCGlobal.shared.buttonMoreMore || namedButtonMore == NCGlobal.shared.buttonMoreLock {
+ toggleMenu(metadata: metadata, image: image)
+ } else if namedButtonMore == NCGlobal.shared.buttonMoreStop {
+ Task {
+ await cancelSession(metadata: metadata)
+ }
+ }
}
func tapRichWorkspace(_ sender: Any) {
@@ -743,16 +1194,50 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
didSelectMetadata(metadata, withOcIds: false)
}
- func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonSwitch(_ sender: Any) {
+ guard !isTransitioning else { return }
+ isTransitioning = true
- func longPressGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return }
- func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ if layoutForView.layout == NCGlobal.shared.layoutGrid {
+ layoutForView.layout = NCGlobal.shared.layoutList
+ } else {
+ layoutForView.layout = NCGlobal.shared.layoutGrid
+ }
+ self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
+ self.collectionView.reloadData()
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false }
+ }
- func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonOrder(_ sender: Any) {
+
+// if let titleButtonHeader = NCKeychain().getTitleButtonHeader(account: session.account), !titleButtonHeader.isEmpty {
+// layoutForView?.titleButtonHeader = titleButtonHeader
+// }
+// NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView!)
+
+ let sortMenu = NCSortMenu()
+ sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
+ }
- func longPressMoreGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { }
+ func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressMoreGridItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func tapButtonTransfer(_ sender: Any) { }
+
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
+
@objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) {
openMenuItems(with: nil, gestureRecognizer: gestureRecognizer)
}
@@ -807,11 +1292,27 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - DataSource
- @MainActor
- func reloadDataSource() async {
- if !isSearchingMode {
- Task.detached {
- if await self.isRecommendationActived() {
+ @objc func reloadDataSource() async {
+
+ // get auto upload folder
+ autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
+ autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account)
+
+ // get layout for view
+ layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
+ // set GroupField for Grid
+ if !isSearchingMode && layoutForView?.layout == NCGlobal.shared.layoutGrid {
+ groupByField = "classFile"
+ } else {
+ groupByField = "name"
+ }
+
+ if isSearchingMode {
+ isDirectoryEncrypted = false
+ } else {
+ isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
+ if isRecommendationActived {
+ Task.detached {
await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
}
}
@@ -823,7 +1324,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
animations: { self.collectionView.reloadData() },
completion: nil)
- await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ self.refreshControl.endRefreshing()
+ self.collectionView.reloadData()
+ self.setNavigationRightItems()
+ }
}
func getServerData(forced: Bool = false) async { }
@@ -955,39 +1460,56 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
- // MARK: - Header size
-
- func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
- heightHeaderRecommendations: CGFloat,
- heightHeaderSection: CGFloat) {
- var heightHeaderRichWorkspace: CGFloat = 0
- var heightHeaderRecommendations: CGFloat = 0
- var heightHeaderSection: CGFloat = 0
+ func pushViewController(viewController: UIViewController) {
+ if pushed { return }
- if showDescription,
- !isSearchingMode,
- let richWorkspaceText = self.richWorkspaceText,
- !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
- heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
- }
-
- if isRecommendationActived,
- !isSearchingMode,
- NCPreferences().showRecommendedFiles,
- !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
- heightHeaderRecommendations = self.heightHeaderRecommendations
- heightHeaderSection = self.heightHeaderSection
- }
+ pushed = true
+ navigationController?.pushViewController(viewController, animated: true)
+ }
+
+ // MARK: - Header size
- if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
- if section == 0 {
- return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
- } else {
- return (0, 0, self.heightHeaderSection)
- }
- } else {
- return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
+// heightHeaderRecommendations: CGFloat,
+// heightHeaderSection: CGFloat) {
+// var heightHeaderRichWorkspace: CGFloat = 0
+// var heightHeaderRecommendations: CGFloat = 0
+// var heightHeaderSection: CGFloat = 0
+//
+// if showDescription,
+// !isSearchingMode,
+// let richWorkspaceText = self.richWorkspaceText,
+// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
+// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
+// }
+//
+// if isRecommendationActived,
+// !isSearchingMode,
+// NCKeychain().showRecommendedFiles,
+// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
+// heightHeaderRecommendations = self.heightHeaderRecommendations
+// heightHeaderSection = self.heightHeaderSection
+// }
+//
+// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
+// if section == 0 {
+// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
+// } else {
+// return (0, 0, self.heightHeaderSection)
+// }
+// } else {
+// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+// }
+// }
+
+ func isHeaderMenuTransferViewEnabled() -> [tableMetadata]? {
+ if headerMenuTransferView,
+ NCNetworking.shared.isOnline,
+ let results = database.getResultsMetadatas(predicate: NSPredicate(format: "status IN %@", [global.metadataStatusWaitUpload, global.metadataStatusUploading])),
+ !results.isEmpty {
+ return Array(results)
}
+ return nil
}
func sizeForHeaderInSection(section: Int) -> CGSize {
@@ -996,7 +1518,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let isIphone = UIDevice.current.userInterfaceIdiom == .phone
if self.dataSource.isEmpty() {
- height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset)
+ height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil)
} else if isEditMode || (isLandscape && isIphone) {
return CGSize.zero
} else {
From 1395f7b467815e9f4cf51037b183256816e56900 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 22:00:01 +0530
Subject: [PATCH 023/177] NMC 1984 - Privacy policy customisation added
---
.../NCCollectionViewCommon.swift | 758 +++++++-----------
1 file changed, 268 insertions(+), 490 deletions(-)
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 2a8cb39d2a..f3773d3ecf 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -4,11 +4,13 @@
import UIKit
import SwiftUI
+import Realm
import RealmSwift
import NextcloudKit
import EasyTipView
+import MoEngageInApps
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, NCTransferDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
@@ -18,7 +20,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let utilityFileSystem = NCUtilityFileSystem()
let imageCache = NCImageCache.shared
var dataSource = NCCollectionViewDataSource()
- let networking = NCNetworking.shared
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
var pinchGesture: UIPinchGestureRecognizer = UIPinchGestureRecognizer()
@@ -29,7 +30,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var backgroundImageView = UIImageView()
var serverUrl: String = ""
var isEditMode = false
- var isDirectoryE2EE = false
+ var isDirectoryEncrypted = false
var fileSelect: [String] = []
var metadataFolder: tableMetadata?
var richWorkspaceText: String?
@@ -38,7 +39,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var isSearchingMode: Bool = false
var networkSearchInProgress: Bool = false
var layoutForView: NCDBLayoutForView?
- var searchDataSourceTask: URLSessionTask?
+ var dataSourceTask: URLSessionTask?
var providers: [NKSearchProvider]?
var searchResults: [NKSearchResult]?
var listLayout = NCListLayout()
@@ -49,18 +50,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var tabBarSelect: NCCollectionViewCommonSelectTabBar?
var attributesZoomIn: UIMenuElement.Attributes = []
var attributesZoomOut: UIMenuElement.Attributes = []
- var tipViewAccounts: EasyTipView?
+ let maxImageGrid: CGFloat = 7
+// var headerMenu: NCSectionFirstHeader?
+ var tipViewAccounts: EasyTipView?
+ var tipViewAutoUpload: EasyTipView?
+ var headerMenu: NCSectionHeaderMenu?
+ var headerMenuTransferView = false
+ var headerMenuButtonsView: Bool = true
+ var headerRichWorkspaceDisable: Bool = false
+
// DECLARE
var layoutKey = ""
var titleCurrentFolder = ""
var titlePreviusFolder: String?
var enableSearchBar: Bool = false
- let maxImageGrid: CGFloat = 7
- var headerMenu: NCSectionHeaderMenu?
- var headerMenuTransferView = false
- var headerMenuButtonsView: Bool = true
- var headerRichWorkspaceDisable: Bool = false
var groupByField = "name"
@@ -93,7 +97,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let heightHeaderRecommendations: CGFloat = 160
let heightHeaderSection: CGFloat = 30
- @MainActor
var session: NCSession.Session {
NCSession.shared.getSession(controller: tabBarController)
}
@@ -111,29 +114,30 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
var showDescription: Bool {
- !headerRichWorkspaceDisable && NCPreferences().showDescription
+ !headerRichWorkspaceDisable && NCKeychain().showDescription
}
var isRecommendationActived: Bool {
- let capabilities = NCNetworking.shared.capabilities[session.account] ?? NKCapabilities.Capabilities()
- return self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && capabilities.recommendations
+ self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) &&
+ NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations
}
var infoLabelsSeparator: String {
layoutForView?.layout == global.layoutList ? " - " : ""
}
- @MainActor
var controller: NCMainTabBarController? {
self.tabBarController as? NCMainTabBarController
}
- var mainNavigationController: NCMainNavigationController? {
- self.navigationController as? NCMainNavigationController
+ var defaultPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, NCGlobal.shared.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
}
- var sceneIdentifier: String {
- (self.tabBarController as? NCMainTabBarController)?.sceneIdentifier ?? ""
+ var personalFilesOnlyPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
}
var isNumberOfItemsInAllSectionsNull: Bool {
@@ -156,18 +160,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
return pinchGesture.state == .began || pinchGesture.state == .changed
}
- func isRecommendationActived() async -> Bool {
- let capabilities = await NKCapabilities.shared.getCapabilities(for: session.account)
- return self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && capabilities.recommendations
- }
-
- internal let debouncer = NCDebouncer(delay: 1)
-
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
+ tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, delegate: self)
self.navigationController?.presentationController?.delegate = self
collectionView.alwaysBounceVertical = true
collectionView.accessibilityIdentifier = "NCCollectionViewCommon"
@@ -207,16 +205,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView.refreshControl = refreshControl
refreshControl.action(for: .valueChanged) { _ in
- Task { @MainActor in
- // Perform async server forced
- await self.getServerData(forced: true)
-
- // Stop the refresh control after data is loaded
- self.refreshControl.endRefreshing()
-
- // Wait 1.5 seconds before resetting the button alpha
- try? await Task.sleep(nanoseconds: 1_500_000_000)
- self.mainNavigationController?.resetPlusButtonAlpha()
+ self.dataSource.removeAll()
+ self.getServerData()
+ if self.isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ self.refreshControl.endRefreshing()
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
+ self.resetPlusButtonAlpha()
}
}
@@ -247,17 +245,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String
UserDefaults.standard.set(appVersion, forKey: "CurrentAppVersion")
}
-
- registerForTraitChanges([UITraitUserInterfaceStyle.self]) { [weak self] (view: NCCollectionViewCommon, _) in
- guard let self else { return }
-
- self.sectionFirstHeader?.setRichWorkspaceColor(style: view.traitCollection.userInterfaceStyle)
- }
-
- NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: self.global.notificationCenterChangeTheming), object: nil, queue: .main) { [weak self] _ in
- guard let self else { return }
- self.collectionView.reloadData()
- }
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeTheming), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadDataSource), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(getServerData(_:)), name: NSNotification.Name(rawValue: global.notificationCenterGetServerData), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(reloadHeader(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadHeader), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(updateIcons), name: NSNotification.Name(rawValue: global.notificationCenterUpdateIcons), object: nil)
DispatchQueue.main.async {
self.collectionView?.collectionViewLayout.invalidateLayout()
@@ -267,6 +260,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
+ AnalyticsHelper.shared.displayInAppNotification()
+
if titlePreviusFolder != nil {
navigationController?.navigationBar.topItem?.title = titlePreviusFolder
}
@@ -276,11 +271,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
navigationController?.setNavigationBarHidden(false, animated: true)
appDelegate.activeViewController = self
+ appDelegate.account = session.account
+ appDelegate.urlBase = session.urlBase
+ appDelegate.userId = session.userId
+ appDelegate.user = session.user
- if tabBarSelect == nil {
- tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, viewController: self, delegate: self)
- }
-
+ NCKeychain().setAccountName(account: session.account)
isEditMode = false
/// Magentacloud branding changes hide user account button on left navigation bar
@@ -309,9 +305,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- Task {
- await NCNetworking.shared.transferDispatcher.addDelegate(self)
- }
+ NCNetworking.shared.transferDelegate = self
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(closeRichWorkspaceWebView), name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
@@ -344,22 +338,20 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
- self.networking.cancelUnifiedSearchFiles()
+ NCNetworking.shared.cancelUnifiedSearchFiles()
dismissTip()
pushed = false
toggleSelect(isOn: false)
// Cancel Queue & Retrieves Properties
- self.networking.downloadThumbnailQueue.cancelAll()
- self.networking.unifiedSearchQueue.cancelAll()
- searchDataSourceTask?.cancel()
+ NCNetworking.shared.downloadThumbnailQueue.cancelAll()
+ NCNetworking.shared.unifiedSearchQueue.cancelAll()
+ dataSourceTask?.cancel()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
- Task {
- await NCNetworking.shared.transferDispatcher.removeDelegate(self)
- }
+ NCNetworking.shared.transferDelegate = nil
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
@@ -374,6 +366,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
@@ -385,7 +382,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
- removeImageCache(metadatas: self.dataSource.getMetadatas())
+ dataSource.removeImageCache()
}
func isApplicationUpdated() -> Bool {
@@ -426,250 +423,68 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
return true
}
- // MARK: - Transfer Delegate
-
- func transferProgressDidUpdate(progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String) { }
+ override func viewWillLayoutSubviews() {
+ super.viewWillLayoutSubviews()
- func transferChange(status: String, metadatasError: [tableMetadata: NKError]) {
- switch status {
- // DELETE
- case self.global.networkingStatusDelete:
- let errorForThisServer = metadatasError.first { entry in
- let (key, value) = entry
- return key.serverUrl == self.serverUrl && value != .success
- }?.value
-
- let needLoadDataSource = metadatasError.contains { entry in
- let (key, value) = entry
- return key.serverUrl == self.serverUrl && value == .success
- }
-
- if let error = errorForThisServer {
- NCContentPresenter().showError(error: error)
- }
-
- if self.isSearchingMode {
- self.networkSearch()
- } else if needLoadDataSource {
- Task {
- await self.reloadDataSource()
- }
- } else {
- Task.detached {
- if await self.isRecommendationActived() {
- await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
- }
- }
- }
- default:
- break
- }
- }
-
- func transferChange(status: String, metadata: tableMetadata, error: NKError) {
- guard session.account == metadata.account else { return }
-
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- DispatchQueue.main.async {
- switch status {
- // UPLOADED, UPLOADED LIVEPHOTO
- case self.global.networkingStatusUploaded, self.global.networkingStatusUploadedLivePhoto:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- // DOWNLOAD
- case self.global.networkingStatusDownloading:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- case self.global.networkingStatusDownloaded:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- case self.global.networkingStatusDownloadCancel:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- // CREATE FOLDER
- case self.global.networkingStatusCreateFolder:
- if metadata.serverUrl == self.serverUrl, metadata.sessionSelector != self.global.selectorUploadAutoUpload {
- self.pushMetadata(metadata)
- }
- // RENAME
- case self.global.networkingStatusRename:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- // FAVORITE
- case self.global.networkingStatusFavorite:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self is NCFavorite {
- Task {
- await self.reloadDataSource()
- }
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- default:
- break
- }
- }
- }
-
- func transferReloadData(serverUrl: String?, status: Int?) {
- self.debouncer.call {
- if self.isSearchingMode {
- guard status != self.global.metadataStatusWaitDelete,
- status != self.global.metadataStatusWaitRename,
- status != self.global.metadataStatusWaitMove,
- status != self.global.metadataStatusWaitCopy,
- status != self.global.metadataStatusWaitFavorite else {
- return
- }
- self.networkSearch()
- } else if ( self.serverUrl == serverUrl) || serverUrl == nil {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- }
-
- func transferRequestData(serverUrl: String?) {
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if ( self.serverUrl == serverUrl) || serverUrl == nil {
- Task {
- await self.getServerData()
- }
- }
- }
- }
-
- func transferCopy(metadata: tableMetadata, destination: String, error: NKError) {
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- if isSearchingMode {
- return networkSearch()
- }
-
- if metadata.serverUrl == self.serverUrl || destination == self.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
-
- func transferMove(metadata: tableMetadata, destination: String, error: NKError) {
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- if isSearchingMode {
- return networkSearch()
- }
-
- if metadata.serverUrl == self.serverUrl || destination == self.serverUrl {
- Task {
- await self.reloadDataSource()
- }
+ if let frame = tabBarController?.tabBar.frame {
+ tabBarSelect?.hostingController?.view.frame = frame
}
}
// MARK: - NotificationCenter
@objc func applicationWillResignActive(_ notification: NSNotification) {
- mainNavigationController?.resetPlusButtonAlpha()
+ self.resetPlusButtonAlpha()
+ self.refreshControl.endRefreshing()
}
- @objc func closeRichWorkspaceWebView() {
- Task {
- await self.reloadDataSource()
+ @objc func reloadAvatar(_ notification: NSNotification) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+ self.showTipAccounts()
}
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError,
+ error.errorCode != global.errorNotModified else { return }
+ /// Magentacloud branding changes hide user account button on left navigation bar
+ setNavigationLeftItems()
}
- // MARK: - Layout
-
- func changeLayout(layoutForView: NCDBLayoutForView) {
- let homeServer = utilityFileSystem.getHomeServer(urlBase: session.urlBase, userId: session.userId)
- let numFoldersLayoutsForView = self.database.getLayoutsForView(keyStore: layoutForView.keyStore)?.count ?? 1
-
- func changeLayout(withSubFolders: Bool) {
- if self.layoutForView?.layout == layoutForView.layout {
- self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView, withSubFolders: withSubFolders)
- Task {
- await self.reloadDataSource()
- }
- return
- }
-
- self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView, withSubFolders: withSubFolders)
- layoutForView.layout = layoutForView.layout
- self.layoutType = layoutForView.layout
-
- collectionView.reloadData()
-
- switch layoutForView.layout {
- case global.layoutList:
- self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
- case global.layoutGrid:
- self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
- case global.layoutPhotoSquare, global.layoutPhotoRatio:
- self.collectionView.setCollectionViewLayout(self.mediaLayout, animated: true)
- default:
- break
- }
+ @objc func changeTheming(_ notification: NSNotification) {
+ self.reloadDataSource()
+ }
- self.collectionView.collectionViewLayout.invalidateLayout()
+ @objc func changeLayout(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let layoutForView = userInfo["layoutForView"] as? NCDBLayoutForView,
+ account == session.account,
+ serverUrl == self.serverUrl
+ else { return }
- Task {
- await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
- }
+ if self.layoutForView?.layout == layoutForView.layout {
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ self.reloadDataSource()
+ return
}
- if serverUrl == homeServer || numFoldersLayoutsForView == 1 {
- changeLayout(withSubFolders: false)
- } else {
- let alertController = UIAlertController(title: NSLocalizedString("_propagate_layout_", comment: ""), message: nil, preferredStyle: .alert)
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ layoutForView.layout = layoutForView.layout
+ self.layoutType = layoutForView.layout
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
- changeLayout(withSubFolders: true)
- }))
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in
- changeLayout(withSubFolders: false)
- }))
+ collectionView.reloadData()
- self.present(alertController, animated: true)
+ switch layoutForView.layout {
+ case global.layoutList:
+ self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
+ case global.layoutGrid:
+ self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
+ case global.layoutPhotoSquare, global.layoutPhotoRatio:
+ self.collectionView.setCollectionViewLayout(self.mediaLayout, animated: true)
+ default:
+ break
}
+
self.collectionView.collectionViewLayout.invalidateLayout()
// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
@@ -934,16 +749,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView?.reloadData()
}
}
-
- @objc func updateShare(_ notification: NSNotification) {
- if isSearchingMode {
- networkSearch()
- } else {
- self.dataSource.removeAll()
- getServerData()
- }
- }
-
+
@objc func triggerProgressTask(_ notification: NSNotification) {
guard let userInfo = notification.userInfo as NSDictionary?,
let progressNumber = userInfo["progress"] as? NSNumber,
@@ -958,14 +764,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected))
-
- // HEADER
-// if self.headerMenuTransferView, transfer.session.contains("upload") {
-// self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
-// self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
-// }
- DispatchQueue.main.async {
+// DispatchQueue.main.async {
if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
if NCNetworking.shared.transferInForegorund?.ocId == ocId {
NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
@@ -975,7 +775,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue
} else {
- guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath,
+ guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId),
let cell = self.collectionView?.cellForItem(at: indexPath),
let cell = cell as? NCCellProtocol else { return }
if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
@@ -1007,8 +807,22 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
+// }
+ }
+
+ @objc func updateShare(_ notification: NSNotification) {
+ if isSearchingMode {
+ networkSearch()
+ } else {
+// self.dataSource.removeAll()
+ getServerData()
}
}
+
+ @objc func updateIcons() {
+ collectionView.reloadData()
+// reloadDataSource()
+ }
// MARK: - Layout
@@ -1017,45 +831,19 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func getNavigationTitle() -> String {
- let tblAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
- if let tblAccount,
- !tblAccount.alias.isEmpty {
- return tblAccount.alias
+ let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
+ if let tableAccount,
+ !tableAccount.alias.isEmpty {
+ return tableAccount.alias
}
return NCBrandOptions.shared.brand
}
- func accountSettingsDidDismiss(tblAccount: tableAccount?, controller: NCMainTabBarController?) { }
-
- @MainActor
- func showLoadingTitle() {
- // Don't show spinner on iPad root folder
- if UIDevice.current.userInterfaceIdiom == .pad,
- (self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session)) || self.serverUrl.isEmpty {
- return
- }
+ func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { }
- let spinner = UIActivityIndicatorView(style: .medium)
- spinner.startAnimating()
+ func resetPlusButtonAlpha(animated: Bool = true) { }
- let container = UIView()
- container.translatesAutoresizingMaskIntoConstraints = false
- container.addSubview(spinner)
-
- spinner.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- spinner.centerXAnchor.constraint(equalTo: container.centerXAnchor),
- spinner.centerYAnchor.constraint(equalTo: container.centerYAnchor)
- ])
-
- self.navigationItem.titleView = container
- }
-
- @MainActor
- func restoreDefaultTitle() {
- self.navigationItem.titleView = nil
- self.navigationItem.title = self.titleCurrentFolder
- }
+ func isHiddenPlusButton(_ isHidden: Bool) { }
// MARK: - Empty
@@ -1096,7 +884,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
searchController?.searchBar.alpha = 1
} else {
searchController?.searchBar.alpha = 0.3
-
}
}
@@ -1108,13 +895,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
isSearchingMode = true
self.providers?.removeAll()
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
+ self.reloadDataSource()
// TIP
dismissTip()
- //
- mainNavigationController?.hiddenPlusButton(true)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
@@ -1124,17 +907,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
- self.networking.cancelUnifiedSearchFiles()
-
+ NCNetworking.shared.cancelUnifiedSearchFiles()
self.isSearchingMode = false
self.literalSearch = ""
self.providers?.removeAll()
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
- //
- mainNavigationController?.hiddenPlusButton(false)
+ self.reloadDataSource()
}
// MARK: - TAP EVENT
@@ -1142,6 +920,10 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
+
+ func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ }
func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
@@ -1150,7 +932,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) {
if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
- NCDownloadAction.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""])
appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue))
NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
@@ -1168,6 +949,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
+
+ func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
+// toggleMenu(metadata: metadata, image: image)
+ Task {
+ await cancelSession(metadata: metadata)
+ }
+ }
+
+ func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func tapRichWorkspace(_ sender: Any) {
if let navigationController = UIStoryboard(name: "NCViewerRichWorkspace", bundle: nil).instantiateInitialViewController() as? UINavigationController {
@@ -1182,8 +973,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
- func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?, sender: Any?) {
- toggleMenu(metadata: metadata, image: image, sender: sender)
+ func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) {
+ toggleMenu(metadata: metadata, image: image)
}
func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {
@@ -1212,32 +1003,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func tapButtonOrder(_ sender: Any) {
-
-// if let titleButtonHeader = NCKeychain().getTitleButtonHeader(account: session.account), !titleButtonHeader.isEmpty {
-// layoutForView?.titleButtonHeader = titleButtonHeader
-// }
-// NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView!)
-
let sortMenu = NCSortMenu()
sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
}
- func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonTransfer(_ sender: Any) { }
- func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
- func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { }
+ func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {
+ }
+ func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressMoreGridItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
- func tapButtonTransfer(_ sender: Any) { }
-
- func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
-
@objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) {
openMenuItems(with: nil, gestureRecognizer: gestureRecognizer)
}
@@ -1259,7 +1046,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
becomeFirstResponder()
if !serverUrl.isEmpty {
- listMenuItems.append(UIMenuItem(title: NSLocalizedString("_paste_file_", comment: ""), action: #selector(pasteFilesMenu(_:))))
+ listMenuItems.append(UIMenuItem(title: NSLocalizedString("_paste_file_", comment: ""), action: #selector(pasteFilesMenu)))
}
if !listMenuItems.isEmpty {
@@ -1268,36 +1055,41 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
+ // MARK: - Transfer Delegate
+
+ func transferProgressDidUpdate(progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String) { }
+
+ func tranferChange(status: String, metadata: tableMetadata, error: NKError) { }
+
// MARK: - Menu Item
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
- if #selector(pasteFilesMenu(_:)) == action {
+
+ if #selector(pasteFilesMenu) == action {
if !UIPasteboard.general.items.isEmpty, !(metadataFolder?.e2eEncrypted ?? false) {
return true
}
- } else if #selector(copyMenuFile(_:)) == action {
+ } else if #selector(copyMenuFile) == action {
return true
- } else if #selector(moveMenuFile(_:)) == action {
+ } else if #selector(moveMenuFile) == action {
return true
}
return false
}
- @objc func pasteFilesMenu(_ sender: Any?) {
- Task {
- await NCDownloadAction.shared.pastePasteboard(serverUrl: serverUrl, account: session.account, controller: self.controller)
- }
+ @objc func pasteFilesMenu() {
+ NCActionCenter.shared.pastePasteboard(serverUrl: serverUrl, account: session.account, controller: self.controller)
}
// MARK: - DataSource
- @objc func reloadDataSource() async {
+ @objc func reloadDataSource() {
// get auto upload folder
autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account)
-
+
// get layout for view
layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
// set GroupField for Grid
@@ -1306,32 +1098,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
} else {
groupByField = "name"
}
-
if isSearchingMode {
isDirectoryEncrypted = false
} else {
isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
- if isRecommendationActived {
- Task.detached {
- await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
- }
- }
}
- UIView.transition(with: self.collectionView,
- duration: 0.20,
- options: .transitionCrossDissolve,
- animations: { self.collectionView.reloadData() },
- completion: nil)
-
- (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ DispatchQueue.main.async {
+// UIView.transition(with: self.collectionView,
+// duration: 0.20,
+// options: .transitionCrossDissolve,
+// animations: { self.collectionView.reloadData() },
+// completion: nil)
+//
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
self.refreshControl.endRefreshing()
self.collectionView.reloadData()
self.setNavigationRightItems()
}
}
- func getServerData(forced: Bool = false) async { }
+ func getServerData() {
+ }
@objc func networkSearch() {
guard !networkSearchInProgress else {
@@ -1340,68 +1128,45 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
guard !session.account.isEmpty,
let literalSearch = literalSearch,
!literalSearch.isEmpty else {
- return
+ return self.refreshControl.endRefreshing()
}
- let capabilities = NCNetworking.shared.capabilities[session.account] ?? NKCapabilities.Capabilities()
self.networkSearchInProgress = true
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
-
- if capabilities.serverVersionMajor >= global.nextcloudVersion20 {
- self.networking.unifiedSearchFiles(literal: literalSearch, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
- } providers: { account, searchProviders in
+ self.refreshControl.beginRefreshing()
+ self.reloadDataSource()
+
+ if NCCapabilities.shared.getCapabilities(account: session.account).capabilityServerVersionMajor >= global.nextcloudVersion20 {
+ NCNetworking.shared.unifiedSearchFiles(literal: literalSearch, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
+ } providers: { _, searchProviders in
self.providers = searchProviders
self.searchResults = []
- self.dataSource = NCCollectionViewDataSource(metadatas: [],
- layoutForView: self.layoutForView,
- providers: self.providers,
- searchResults: self.searchResults,
- account: account)
+ self.dataSource = NCCollectionViewDataSource(metadatas: [], layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults)
} update: { _, _, searchResult, metadatas in
guard let metadatas, !metadatas.isEmpty, self.isSearchingMode, let searchResult else { return }
- self.networking.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
+ NCNetworking.shared.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
} completion: { _, _ in
- Task {
- await self.reloadDataSource()
- }
+ self.refreshControl.endRefreshing()
+ self.reloadDataSource()
self.networkSearchInProgress = false
}
} else {
- self.networking.searchFiles(literal: literalSearch, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
+ NCNetworking.shared.searchFiles(literal: literalSearch, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
} completion: { metadatasSearch, error in
- Task {
- guard let metadatasSearch,
- error == .success,
- self.isSearchingMode
- else {
- self.networkSearchInProgress = false
- await self.reloadDataSource()
- return
- }
- let ocId = metadatasSearch.map { $0.ocId }
- let metadatas = await self.database.getMetadatasAsync(predicate: NSPredicate(format: "ocId IN %@", ocId),
- withLayout: self.layoutForView,
- withAccount: self.session.account)
-
- self.dataSource = NCCollectionViewDataSource(metadatas: metadatas,
- layoutForView: self.layoutForView,
- providers: self.providers,
- searchResults: self.searchResults,
- account: self.session.account)
- self.networkSearchInProgress = false
- await self.reloadDataSource()
+ DispatchQueue.main.async {
+ self.refreshControl.endRefreshing()
+ self.reloadDataSource()
}
+ guard let metadatasSearch, error == .success, self.isSearchingMode else { return }
+ let ocId = metadatasSearch.map { $0.ocId }
+ let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: self.layoutForView)
+
+ self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults)
+ self.networkSearchInProgress = false
}
}
}
@@ -1412,11 +1177,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
metadataForSection.unifiedSearchInProgress = true
self.collectionView?.reloadData()
- self.networking.unifiedSearchFilesProvider(id: lastSearchResult.id, term: term, limit: 5, cursor: cursor, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
+ NCNetworking.shared.unifiedSearchFilesProvider(id: lastSearchResult.id, term: term, limit: 5, cursor: cursor, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
} completion: { _, searchResult, metadatas, error in
if error != .success {
NCContentPresenter().showError(error: error)
@@ -1435,15 +1198,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - Push metadata
func pushMetadata(_ metadata: tableMetadata) {
- guard let navigationCollectionViewCommon = self.controller?.navigationCollectionViewCommon else {
- return
- }
- let serverUrlPush = utilityFileSystem.createServerUrl(serverUrl: metadata.serverUrl, fileName: metadata.fileName)
-
- // Set Last Opening Date
- Task {
- await database.setDirectoryLastOpeningDateAsync(ocId: metadata.ocId)
- }
+ guard let navigationCollectionViewCommon = self.controller?.navigationCollectionViewCommon else { return }
+ let serverUrlPush = utilityFileSystem.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)
if let viewController = navigationCollectionViewCommon.first(where: { $0.navigationController == self.navigationController && $0.serverUrl == serverUrlPush})?.viewController, viewController.isViewLoaded {
navigationController?.pushViewController(viewController, animated: true)
@@ -1459,38 +1215,55 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
-
+
func pushViewController(viewController: UIViewController) {
if pushed { return }
pushed = true
navigationController?.pushViewController(viewController, animated: true)
}
-
+
// MARK: - Header size
-// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
-// heightHeaderRecommendations: CGFloat,
-// heightHeaderSection: CGFloat) {
-// var heightHeaderRichWorkspace: CGFloat = 0
-// var heightHeaderRecommendations: CGFloat = 0
-// var heightHeaderSection: CGFloat = 0
+// func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
+// var headerRichWorkspace: CGFloat = 0
//
-// if showDescription,
-// !isSearchingMode,
-// let richWorkspaceText = self.richWorkspaceText,
-// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
-// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
-// }
+// func getHeaderHeight() -> CGFloat {
+// var size: CGFloat = 0
//
-// if isRecommendationActived,
-// !isSearchingMode,
-// NCKeychain().showRecommendedFiles,
-// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
-// heightHeaderRecommendations = self.heightHeaderRecommendations
-// heightHeaderSection = self.heightHeaderSection
+// if isHeaderMenuTransferViewEnabled() != nil {
+// if !isSearchingMode {
+// size += global.heightHeaderTransfer
+// }
+// }
+// if headerMenuButtonsView {
+// size += NCGlobal.shared.heightButtonsView
+// }
+// return size
// }
//
+//// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
+//// heightHeaderRecommendations: CGFloat,
+//// heightHeaderSection: CGFloat) {
+//// var heightHeaderRichWorkspace: CGFloat = 0
+//// var heightHeaderRecommendations: CGFloat = 0
+//// var heightHeaderSection: CGFloat = 0
+////
+//// if showDescription,
+//// !isSearchingMode,
+//// let richWorkspaceText = self.richWorkspaceText,
+//// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
+//// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
+//// }
+////
+//// if isRecommendationActived,
+//// !isSearchingMode,
+//// NCKeychain().showRecommendedFiles,
+//// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
+//// heightHeaderRecommendations = self.heightHeaderRecommendations
+//// heightHeaderSection = self.heightHeaderSection
+//// }
+//
// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
// if section == 0 {
// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
@@ -1499,6 +1272,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// }
// } else {
// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+////
+//// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
+//// if section == 0 {
+//// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
+//// } else {
+//// return (0, 0, self.heightHeaderSection)
+//// }
+//// } else {
+//// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
// }
// }
@@ -1522,8 +1304,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
} else if isEditMode || (isLandscape && isIphone) {
return CGSize.zero
} else {
- let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: section)
- height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderSection
+ let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section)
+ height = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection
}
return CGSize(width: collectionView.frame.width, height: height)
@@ -1532,25 +1314,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - Footer size
func sizeForFooterInSection(section: Int) -> CGSize {
- guard let controller else {
- return CGSize.zero
- }
let sections = dataSource.numberOfSections()
- let bottomAreaInsets: CGFloat = controller.tabBar.safeAreaInsets.bottom == 0 ? 34 : 0
- let height = controller.tabBar.frame.height + bottomAreaInsets
-
- if isEditMode {
- return CGSize(width: collectionView.frame.width, height: 90 + height)
- }
-
- if isSearchingMode {
- return CGSize(width: collectionView.frame.width, height: 50)
- }
+ let metadataForSection = self.dataSource.getMetadataForSection(section)
+ let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+ let metadatasCount: Int = metadataForSection?.lastSearchResult?.entries.count ?? 0
+ var size = CGSize(width: collectionView.frame.width, height: 0)
if section == sections - 1 {
- return CGSize(width: collectionView.frame.width, height: height)
+ size.height += 85
} else {
- return CGSize(width: collectionView.frame.width, height: 0)
+ size.height += 1
+ }
+
+ if isSearchingMode && isPaginated && metadatasCount > 0 {
+ size.height += 30
}
+ return size
}
}
From d1d98c0e5bcd899d89147f5a8ed489d4c218b7a6 Mon Sep 17 00:00:00 2001
From: Shweta Waikar
Date: Tue, 13 Jun 2023 14:33:11 +0530
Subject: [PATCH 024/177] NMC 2267 Rename file/folder customisation
---
iOSClient/Rename file/NCRenameFile.storyboard | 135 +++++++++
iOSClient/Rename file/NCRenameFile.swift | 263 ++++++++++++++++++
2 files changed, 398 insertions(+)
create mode 100644 iOSClient/Rename file/NCRenameFile.storyboard
create mode 100644 iOSClient/Rename file/NCRenameFile.swift
diff --git a/iOSClient/Rename file/NCRenameFile.storyboard b/iOSClient/Rename file/NCRenameFile.storyboard
new file mode 100644
index 0000000000..6e98f850cc
--- /dev/null
+++ b/iOSClient/Rename file/NCRenameFile.storyboard
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Rename file/NCRenameFile.swift b/iOSClient/Rename file/NCRenameFile.swift
new file mode 100644
index 0000000000..723e02ed3f
--- /dev/null
+++ b/iOSClient/Rename file/NCRenameFile.swift
@@ -0,0 +1,263 @@
+//
+// NCRenameFile.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 26/02/21.
+// Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// 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
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import NextcloudKit
+
+public protocol NCRenameFileDelegate: AnyObject {
+ func rename(fileName: String, fileNameNew: String)
+}
+
+// optional func
+public extension NCRenameFileDelegate {
+ func rename(fileName: String, fileNameNew: String) {}
+}
+
+class NCRenameFile: UIViewController, UITextFieldDelegate {
+
+ @IBOutlet weak var titleLabel: UILabel!
+ @IBOutlet weak var separatorHeightContraint: NSLayoutConstraint!
+ @IBOutlet weak var previewFile: UIImageView!
+ @IBOutlet weak var fileNameNoExtension: UITextField!
+ @IBOutlet weak var point: UILabel!
+ @IBOutlet weak var ext: UITextField!
+ @IBOutlet weak var fileNameNoExtensionTrailingContraint: NSLayoutConstraint!
+ @IBOutlet weak var cancelButton: UIButton!
+ @IBOutlet weak var renameButton: UIButton!
+ @IBOutlet weak var seperator: UIView!
+
+ let width: CGFloat = 300
+ let height: CGFloat = 350
+
+ var metadata: tableMetadata?
+ var indexPath: IndexPath = IndexPath()
+ var fileName: String?
+ var imagePreview: UIImage?
+ var disableChangeExt: Bool = false
+ weak var delegate: NCRenameFileDelegate?
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ if let metadata = self.metadata {
+
+ if metadata.directory {
+ titleLabel.text = NSLocalizedString("_rename_folder_", comment: "")
+ } else {
+ titleLabel.text = NSLocalizedString("_rename_file_", comment: "")
+ }
+ separatorHeightContraint.constant = 0.3
+ seperator.backgroundColor = NCBrandColor.shared.seperatorRename
+ fileNameNoExtension.backgroundColor = .secondarySystemGroupedBackground
+ fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension
+ fileNameNoExtension.delegate = self
+ fileNameNoExtension.becomeFirstResponder()
+
+ ext.text = metadata.fileExtension
+ ext.delegate = self
+ if disableChangeExt {
+ ext.isEnabled = false
+ ext.textColor = .lightGray
+ }
+
+ previewFile.image = imagePreview
+ previewFile.layer.cornerRadius = 10
+ previewFile.layer.masksToBounds = true
+
+ if metadata.directory {
+
+ if imagePreview == nil {
+ previewFile.image = NCImageCache.images.folder
+ }
+
+ ext.isHidden = true
+ point.isHidden = true
+ fileNameNoExtensionTrailingContraint.constant = 20
+
+ } else {
+
+ if imagePreview == nil {
+ previewFile.image = NCImageCache.images.file
+ }
+
+ fileNameNoExtensionTrailingContraint.constant = 90
+ }
+
+ } else if let fileName = self.fileName {
+
+ titleLabel.text = NSLocalizedString("_rename_file_", comment: "")
+
+ fileNameNoExtension.text = (fileName as NSString).deletingPathExtension
+ fileNameNoExtension.delegate = self
+ fileNameNoExtension.becomeFirstResponder()
+ fileNameNoExtensionTrailingContraint.constant = 90
+
+ ext.text = (fileName as NSString).pathExtension
+ ext.delegate = self
+
+ if imagePreview == nil {
+ previewFile.image = NCImageCache.images.file
+ } else {
+ previewFile.image = imagePreview
+ }
+ previewFile.layer.cornerRadius = 10
+ previewFile.layer.masksToBounds = true
+ }
+
+ cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal)
+ cancelButton.setTitleColor(NCBrandColor.shared.iconColor, for: .normal)
+ cancelButton.layer.cornerRadius = 5
+ cancelButton.layer.masksToBounds = true
+ cancelButton.layer.borderWidth = 0.3
+ cancelButton.layer.borderColor = NCBrandColor.shared.iconColor.cgColor
+
+ renameButton.setTitle(NSLocalizedString("_rename_", comment: ""), for: .normal)
+ renameButton.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
+ renameButton.layer.cornerRadius = 5
+ renameButton.layer.masksToBounds = true
+ renameButton.layer.backgroundColor = NCBrandColor.shared.brand.cgColor
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ if metadata == nil && fileName == nil {
+ dismiss(animated: true)
+ }
+
+ fileNameNoExtension.selectAll(nil)
+ }
+
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
+
+ textField.resignFirstResponder()
+ renameFile(textField)
+ return true
+ }
+
+ // MARK: - Action
+
+ @IBAction func cancel(_ sender: Any) {
+
+ dismiss(animated: true)
+ }
+
+ @IBAction func renameFile(_ sender: Any) {
+
+ var fileNameNoExtensionNew = ""
+ var extNew = ""
+ var fileNameNew = ""
+
+ if let metadata = self.metadata {
+
+ let extCurrent = (metadata.fileNameView as NSString).pathExtension
+
+ if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 {
+ return self.fileNameNoExtension.text = (metadata.fileNameView as NSString).deletingPathExtension
+ } else {
+ fileNameNoExtensionNew = fileNameNoExtension.text!
+ }
+
+ if metadata.directory {
+
+ fileNameNew = fileNameNoExtensionNew
+ renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath)
+
+ } else {
+
+ if ext.text == nil || ext.text?.count == 0 {
+ return self.ext.text = metadata.fileExtension
+ } else {
+ extNew = ext.text!
+ }
+
+ if extNew != extCurrent {
+
+ let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent)
+ let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert)
+
+ var title = NSLocalizedString("_use_", comment: "") + " ." + extNew
+ alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
+
+ fileNameNew = fileNameNoExtensionNew + "." + extNew
+ self.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: self.indexPath)
+ }))
+
+ title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent
+ alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
+ self.ext.text = metadata.fileExtension
+ }))
+
+ self.present(alertController, animated: true)
+
+ } else {
+
+ fileNameNew = fileNameNoExtensionNew + "." + extNew
+ renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath)
+ }
+ }
+
+ } else if let fileName = self.fileName {
+
+ if fileNameNoExtension.text == nil || fileNameNoExtension.text?.count == 0 {
+ return fileNameNoExtension.text = (fileName as NSString).deletingPathExtension
+ } else if ext.text == nil || ext.text?.count == 0 {
+ return ext.text = (fileName as NSString).pathExtension
+ }
+
+ fileNameNew = (fileNameNoExtension.text ?? "") + "." + (ext.text ?? "")
+ self.dismiss(animated: true) {
+ self.delegate?.rename(fileName: fileName, fileNameNew: fileNameNew)
+ }
+ }
+ }
+
+ // MARK: - Networking
+
+ func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, indexPath: IndexPath) {
+
+ // verify if already exists
+ if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil {
+ NCContentPresenter().showError(error: NKError(errorCode: 0, errorDescription: "_rename_already_exists_"))
+ return
+ }
+
+ NCActivityIndicator.shared.start()
+
+ NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew, indexPath: indexPath, viewController: self) { error in
+
+ NCActivityIndicator.shared.stop()
+
+ if error == .success {
+
+ self.dismiss(animated: true)
+
+ } else {
+
+ NCContentPresenter().showError(error: error)
+ }
+ }
+ }
+}
From 86198883b466053f806a689c6b66a43b571f28eb Mon Sep 17 00:00:00 2001
From: Shweta Waikar
Date: Wed, 14 Jun 2023 12:42:25 +0530
Subject: [PATCH 025/177] NMC 2267 - Unit test cases added for rename file
---
.../NextcloudUnitTests/RenameFileTests.swift | 108 ++++++++++++++++++
1 file changed, 108 insertions(+)
create mode 100644 Tests/NextcloudUnitTests/RenameFileTests.swift
diff --git a/Tests/NextcloudUnitTests/RenameFileTests.swift b/Tests/NextcloudUnitTests/RenameFileTests.swift
new file mode 100644
index 0000000000..c0aa06f029
--- /dev/null
+++ b/Tests/NextcloudUnitTests/RenameFileTests.swift
@@ -0,0 +1,108 @@
+//
+// RenameFileTests.swift
+// NextcloudTests
+//
+// Created by A200073704 on 14/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class RenameFileTests: XCTestCase {
+
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+
+ func testStoryboardPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ XCTAssertNotNil(storyboard, "Storyboard 'NCRenameFile' should be present")
+
+ }
+
+ func testRenameButtonPresence() {
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let renameButton = viewController.renameButton
+ XCTAssertNotNil(renameButton, "Rename button should be present")
+ }
+
+ func testRenameButtonBackgroundColor() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let color = NCBrandColor.shared.brand.cgColor
+ let renameButton = viewController.renameButton.layer.backgroundColor
+
+ XCTAssertEqual(renameButton,color, "Rename Button Bcakground Color should be brand")
+ }
+
+ func testCancelButtonPresence() {
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let cancelButton = viewController.cancelButton
+ XCTAssertNotNil(cancelButton, "Cancel button should be present")
+ }
+
+ func testImageViewPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let imageView = viewController.previewFile
+ XCTAssertNotNil(imageView, "UIImageView should be present on the storyboard")
+ }
+
+ func testTextFiledPresence() {
+
+ let storyboard = UIStoryboard(name: "NCRenameFile", bundle: nil)
+ guard let viewController = storyboard.instantiateInitialViewController() as? NCRenameFile else {
+ XCTFail("Failed to instantiate view controller from storyboard")
+ return
+ }
+
+ _ = viewController.view // Load the view
+
+ let textField = viewController.fileNameNoExtension
+ let textFieldExt = viewController.ext
+
+ XCTAssertNotNil(textField, "FileNameNoExtention TextFiled should be present on the storyboard")
+ XCTAssertNotNil(textFieldExt, "Extension TextFiled should be present on the storyboard")
+
+ }
+
+
+
+}
From 439b9db66c21d7f72057c2565d12554f483ba739 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Tue, 27 Feb 2024 12:18:04 +0530
Subject: [PATCH 026/177] NMC 2267 - NMC 2880 File- Renaming file from its
preview doesn't change filename
---
.../NCCollectionViewCommon.swift | 21 +++++++++++++++
iOSClient/NCGlobal.swift | 27 +++++++++++++++++++
2 files changed, 48 insertions(+)
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 6840597719..be69703c24 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -310,6 +310,27 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeStatusFolderE2EE), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeLayout), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterRenameFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
removeImageCache(metadatas: self.dataSource.getMetadatas())
}
diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift
index 4695fc5c36..b139b8e303 100644
--- a/iOSClient/NCGlobal.swift
+++ b/iOSClient/NCGlobal.swift
@@ -265,6 +265,33 @@ final class NCGlobal: Sendable {
let notificationCenterCheckUserDelaultErrorDone = "checkUserDelaultErrorDone" // userInfo: account, controller
let notificationCenterServerDidUpdate = "serverDidUpdate" // userInfo: account
let notificationCenterNetworkReachability = "networkReachability"
+ let notificationCenterUpdateNotification = "updateNotification"
+ let notificationCenterReloadDataSource = "reloadDataSource" // userInfo: serverUrl?, clearDataSource
+ let notificationCenterGetServerData = "getServerData" // userInfo: serverUrl?
+
+ let notificationCenterChangeStatusFolderE2EE = "changeStatusFolderE2EE" // userInfo: serverUrl
+
+ let notificationCenterDownloadStartFile = "downloadStartFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
+ let notificationCenterDownloadedFile = "downloadedFile" // userInfo: ocId, ocIdTransfer, session, session, serverUrl, account, selector, error
+ let notificationCenterDownloadCancelFile = "downloadCancelFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
+
+ let notificationCenterUploadStartFile = "uploadStartFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, sessionSelector
+ let notificationCenterUploadedFile = "uploadedFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, ocIdTransfer, error
+ let notificationCenterUploadedLivePhoto = "uploadedLivePhoto" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, ocIdTransfer, error
+ let notificationCenterUploadCancelFile = "uploadCancelFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
+
+ let notificationCenterProgressTask = "progressTask" // userInfo: account, ocId, ocIdTransfer, session, serverUrl, status, chunk, e2eEncrypted, progress, totalBytes, totalBytesExpected
+
+ let notificationCenterUpdateBadgeNumber = "updateBadgeNumber" // userInfo: counterDownload, counterUpload
+
+ let notificationCenterCreateFolder = "createFolder" // userInfo: ocId, serverUrl, account, withPush, sceneIdentifier
+ let notificationCenterDeleteFile = "deleteFile" // userInfo: [ocId], error
+ let notificationCenterCopyMoveFile = "copyMoveFile" // userInfo: [ocId] serverUrl, account, dragdrop, type (copy, move)
+ let notificationCenterMoveFile = "moveFile" // userInfo: [ocId], [indexPath], error
+ let notificationCenterCopyFile = "copyFile" // userInfo: [ocId], [indexPath], error
+ let notificationCenterRenameFile = "renameFile" // userInfo: serverUrl, account, error
+ let notificationCenterFavoriteFile = "favoriteFile" // userInfo: ocId, serverUrl
+ let notificationCenterFileExists = "fileExists" // userInfo: ocId, fileExists
let notificationCenterMenuSearchTextPDF = "menuSearchTextPDF"
let notificationCenterMenuGotToPageInPDF = "menuGotToPageInPDF"
From 5dad16f26b504c2ae9568c1be43a803661d43423 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 8 Apr 2025 13:42:47 +0530
Subject: [PATCH 027/177] NMC 2267 - Rename file/folder customisation
---
Nextcloud.xcodeproj/project.pbxproj | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index d3dc9dee77..bbd9de3b54 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,11 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B5D45E6D2DA510E000773929 /* NCRenameFile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5D45E6A2DA510E000773929 /* NCRenameFile.storyboard */; };
+ B5D45E6E2DA510E000773929 /* NCRenameFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D45E6B2DA510E000773929 /* NCRenameFile.swift */; };
+ B5D45E702DA5119A00773929 /* RenameFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D45E6F2DA5119A00773929 /* RenameFileTests.swift */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1368,6 +1373,10 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B5D45E6A2DA510E000773929 /* NCRenameFile.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCRenameFile.storyboard; sourceTree = ""; };
+ B5D45E6B2DA510E000773929 /* NCRenameFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCRenameFile.swift; sourceTree = ""; };
+ B5D45E6F2DA5119A00773929 /* RenameFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameFileTests.swift; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2173,6 +2182,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B5D45E6F2DA5119A00773929 /* RenameFileTests.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -2211,6 +2222,15 @@
path = Advanced;
sourceTree = "";
};
+ B5D45E6C2DA510E000773929 /* Rename file */ = {
+ isa = PBXGroup;
+ children = (
+ B5D45E6A2DA510E000773929 /* NCRenameFile.storyboard */,
+ B5D45E6B2DA510E000773929 /* NCRenameFile.swift */,
+ );
+ path = "Rename file";
+ sourceTree = "";
+ };
C0046CDB2A17B98400D87C9D /* NextcloudUITests */ = {
isa = PBXGroup;
children = (
@@ -3390,6 +3410,7 @@
F7381ED9218218A4000B1560 /* Offline */,
F713418B2597513800768D21 /* PushNotification */,
F765F72E25237E3F00391DBE /* Recent */,
+ B5D45E6C2DA510E000773929 /* Rename file */,
F7CADB3D23CCDDA1000EEC78 /* RichWorkspace */,
F76882042C0DD1E7001CF441 /* Settings */,
F758B41E212C516300515F55 /* Scan document */,
@@ -4086,6 +4107,8 @@
F768822D2C0DD1E7001CF441 /* Acknowledgements.rtf in Resources */,
F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */,
F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */,
+ F7CA212E25F1333300826ABB /* NCAccountRequest.storyboard in Resources */,
+ B5D45E6D2DA510E000773929 /* NCRenameFile.storyboard in Resources */,
F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */,
F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */,
F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */,
@@ -4238,6 +4261,7 @@
files = (
AA52EB472D42AC9E0089C348 /* Placeholder.swift in Sources */,
F372087D2BAB4C0F006B5430 /* TestConstants.swift in Sources */,
+ B5D45E702DA5119A00773929 /* RenameFileTests.swift in Sources */,
F78E2D6C29AF02DB0024D4F3 /* Database.swift in Sources */,
F7817CFE29801A3500FFBC65 /* Data+Extension.swift in Sources */,
);
@@ -4964,6 +4988,7 @@
F763D29D2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
F76882252C0DD1E7001CF441 /* NCSettingsAdvancedModel.swift in Sources */,
F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
+ B5D45E6E2DA510E000773929 /* NCRenameFile.swift in Sources */,
F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
F78B87E72B62527100C65ADC /* NCMediaDataSource.swift in Sources */,
F76882272C0DD1E7001CF441 /* NCManageE2EEView.swift in Sources */,
From 88569d30a7abd9d0b75dd9d0ed4aa56bec3083ac Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 18:30:21 +0530
Subject: [PATCH 028/177] NMC 2267 - Rename file/folder customisation changes
---
.../NCCollectionViewCommon.swift | 722 +++++++++++++++++-
iOSClient/NCGlobal.swift | 265 +++++--
2 files changed, 895 insertions(+), 92 deletions(-)
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index be69703c24..44d1a8c75f 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -4,9 +4,11 @@
import UIKit
import SwiftUI
+import Realm
import RealmSwift
import NextcloudKit
import EasyTipView
+import MoEngageInApps
class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionFirstHeaderDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, NCTransferDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate {
@@ -30,6 +32,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var serverUrl: String = ""
var isEditMode = false
var isDirectoryE2EE = false
+ var isDirectoryEncrypted = false
var fileSelect: [String] = []
var metadataFolder: tableMetadata?
var richWorkspaceText: String?
@@ -39,6 +42,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var networkSearchInProgress: Bool = false
var layoutForView: NCDBLayoutForView?
var searchDataSourceTask: URLSessionTask?
+ var dataSourceTask: URLSessionTask?
var providers: [NKSearchProvider]?
var searchResults: [NKSearchResult]?
var listLayout = NCListLayout()
@@ -49,7 +53,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var tabBarSelect: NCCollectionViewCommonSelectTabBar?
var attributesZoomIn: UIMenuElement.Attributes = []
var attributesZoomOut: UIMenuElement.Attributes = []
+ let maxImageGrid: CGFloat = 7
+ // var headerMenu: NCSectionFirstHeader?
+
var tipViewAccounts: EasyTipView?
+ var tipViewAutoUpload: EasyTipView?
+ var headerMenu: NCSectionHeaderMenu?
+ var headerMenuTransferView = false
+ var headerMenuButtonsView: Bool = true
+ var headerRichWorkspaceDisable: Bool = false
+
+ // DECLARE
+ var layoutKey = ""
+ var titleCurrentFolder = ""
+ var titlePreviusFolder: String?
+ var enableSearchBar: Bool = false
// DECLARE
var layoutKey = ""
@@ -58,8 +76,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var enableSearchBar: Bool = false
var headerRichWorkspaceDisable: Bool = false
+ var groupByField = "name"
+
var emptyImageName: String?
var emptyImageColors: [UIColor]?
+ var emptyImage: UIImage?
var emptyTitle: String = ""
var emptyDescription: String = ""
@@ -78,6 +99,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var numberOfColumns: Int = 0
var lastNumberOfColumns: Int = 0
+ var isTransitioning: Bool = false
+ var selectableDataSource: [RealmSwiftObject] { dataSource.getMetadataSourceForAllSections() }
+ var pushed: Bool = false
+ var emptyDataSet: NCEmptyDataSet?
+
let heightHeaderRecommendations: CGFloat = 160
let heightHeaderSection: CGFloat = 30
@@ -124,6 +150,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
(self.tabBarController as? NCMainTabBarController)?.sceneIdentifier ?? ""
}
+ var defaultPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, NCGlobal.shared.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
+ }
+
+ var personalFilesOnlyPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
+ }
+
var isNumberOfItemsInAllSectionsNull: Bool {
var totalItems = 0
for section in 0.. Bool {
+ let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? ""
+ let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion")
+ return currentVersion != appVersion
+ }
+
+ func redirectToPrivacyViewController() {
+ let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController
+ newViewController.modalPresentationStyle = .fullScreen
+ self.present(newViewController, animated: true, completion: nil)
+ }
func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
let viewController = presentationController.presentedViewController
@@ -605,6 +731,413 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.present(alertController, animated: true)
}
}
+
+ override func viewWillLayoutSubviews() {
+ super.viewWillLayoutSubviews()
+
+ if let frame = tabBarController?.tabBar.frame {
+ tabBarSelect?.hostingController?.view.frame = frame
+ }
+ }
+
+ // MARK: - NotificationCenter
+
+// @objc func applicationWillResignActive(_ notification: NSNotification) {
+// self.resetPlusButtonAlpha()
+// self.refreshControl.endRefreshing()
+// }
+
+ @objc func reloadAvatar(_ notification: NSNotification) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+ self.showTipAccounts()
+ }
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError,
+ error.errorCode != global.errorNotModified else { return }
+ /// Magentacloud branding changes hide user account button on left navigation bar
+ setNavigationLeftItems()
+ }
+
+ @objc func changeTheming(_ notification: NSNotification) {
+ self.reloadDataSource()
+ }
+
+ @objc func changeLayout(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let layoutForView = userInfo["layoutForView"] as? NCDBLayoutForView,
+ account == session.account,
+ serverUrl == self.serverUrl
+ else { return }
+
+ if self.layoutForView?.layout == layoutForView.layout {
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ self.reloadDataSource()
+ return
+ }
+
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ layoutForView.layout = layoutForView.layout
+ self.layoutType = layoutForView.layout
+
+ collectionView.reloadData()
+
+ switch layoutForView.layout {
+ case global.layoutList:
+ self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
+ case global.layoutGrid:
+ self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
+ case global.layoutPhotoSquare, global.layoutPhotoRatio:
+ self.collectionView.setCollectionViewLayout(self.mediaLayout, animated: true)
+ default:
+ break
+ }
+
+ self.collectionView.collectionViewLayout.invalidateLayout()
+
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
+ }
+
+ @objc func reloadDataSource(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as? NSDictionary {
+ if let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ if let clearDataSource = userInfo["clearDataSource"] as? Bool, clearDataSource {
+ self.dataSource.removeAll()
+ }
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func getServerData(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ getServerData()
+ }
+
+ @objc func reloadHeader(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ account == session.account
+ else { return }
+
+ self.collectionView.reloadData()
+ }
+
+ @objc func changeStatusFolderE2EE(_ notification: NSNotification) {
+ reloadDataSource()
+ }
+
+ @objc func closeRichWorkspaceWebView() {
+ reloadDataSource()
+ }
+
+ @objc func deleteFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ } else {
+ NCContentPresenter().showError(error: error)
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func copyMoveFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ reloadDataSource()
+ }
+ }
+
+ @objc func renameFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let error = userInfo["error"] as? NKError,
+ account == session.account
+ else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ if error != .success {
+ NCContentPresenter().showError(error: error)
+ }
+ reloadDataSource()
+ } else {
+ collectionView.reloadData()
+ }
+ }
+
+ @objc func createFolder(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account,
+ let withPush = userInfo["withPush"] as? Bool,
+ let metadata = database.getMetadataFromOcId(ocId)
+ else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if metadata.serverUrl + "/" + metadata.fileName == self.serverUrl {
+ reloadDataSource()
+ } else if withPush, metadata.serverUrl == self.serverUrl {
+ reloadDataSource()
+ if let sceneIdentifier = userInfo["sceneIdentifier"] as? String {
+ if sceneIdentifier == controller?.sceneIdentifier {
+ pushMetadata(metadata)
+ }
+ } else {
+ pushMetadata(metadata)
+ }
+ }
+ }
+
+ @objc func favoriteFile(_ notification: NSNotification) {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if self is NCFavorite {
+ return reloadDataSource()
+ }
+
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ serverUrl == self.serverUrl
+ else { return }
+
+ reloadDataSource()
+ }
+
+ @objc func downloadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ !isSearchingMode,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)
+ else { return }
+
+ // Header view trasfer
+ if metadata.isTransferInForeground {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: 0)
+ DispatchQueue.main.async { self.collectionView?.reloadData() }
+ }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedLivePhoto(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func triggerProgressTask(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let progressNumber = userInfo["progress"] as? NSNumber,
+ let totalBytes = userInfo["totalBytes"] as? Int64,
+ let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
+ let ocId = userInfo["ocId"] as? String,
+ let ocIdTransfer = userInfo["ocIdTransfer"] as? String,
+ let session = userInfo["session"] as? String
+ else { return }
+
+ let chunk: Int = userInfo["chunk"] as? Int ?? 0
+ let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
+
+ let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected))
+
+// DispatchQueue.main.async {
+ if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
+ if NCNetworking.shared.transferInForegorund?.ocId == ocId {
+ NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
+ } else {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: progressNumber.floatValue)
+ self.collectionView.reloadData()
+ }
+ self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue
+ } else {
+ guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId),
+ let cell = self.collectionView?.cellForItem(at: indexPath),
+ let cell = cell as? NCCellProtocol else { return }
+ if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
+ cell.fileProgressView?.isHidden = true
+ cell.fileProgressView?.progress = .zero
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore)
+ if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+ cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+ } else {
+ cell.fileInfoLabel?.text = ""
+ cell.fileSubinfoLabel?.text = ""
+ }
+ } else {
+ cell.fileProgressView?.isHidden = false
+ cell.fileProgressView?.progress = progressNumber.floatValue
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop)
+ let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
+ if status == NCGlobal.shared.metadataStatusDownloading {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else if status == NCGlobal.shared.metadataStatusUploading {
+ if totalBytes > 0 {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ …"
+ }
+ }
+ }
+ }
+// }
+ }
+
+ @objc func updateShare(_ notification: NSNotification) {
+ if isSearchingMode {
+ networkSearch()
+ } else {
+// self.dataSource.removeAll()
+ getServerData()
+ }
+ }
+
+ @objc func updateIcons() {
+ collectionView.reloadData()
+// reloadDataSource()
+ }
+
+ // MARK: - Layout
+
+ func setNavigationLeftItems() {
+ navigationItem.title = titleCurrentFolder
+ }
func getNavigationTitle() -> String {
let tblAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
@@ -646,6 +1179,42 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.navigationItem.titleView = nil
self.navigationItem.title = self.titleCurrentFolder
}
+
+ func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { }
+
+ func resetPlusButtonAlpha(animated: Bool = true) { }
+
+ func isHiddenPlusButton(_ isHidden: Bool) { }
+
+ // MARK: - Empty
+
+ func emptyDataSetView(_ view: NCEmptyView) {
+
+ self.emptyDataSet?.setOffset(getHeaderHeight())
+ if isSearchingMode {
+ view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ if self.dataSourceTask?.state == .running {
+ view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
+ } else {
+ view.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "")
+ }
+ view.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "")
+ } else if self.dataSourceTask?.state == .running {
+ view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+ view.emptyDescription.text = ""
+ } else {
+ if serverUrl.isEmpty {
+ view.emptyImage.image = emptyImage
+ view.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "")
+ view.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "")
+ } else {
+ view.emptyImage.image = UIImage(named: "folder_nmcloud")
+ view.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "")
+ view.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "")
+ }
+ }
+ }
// MARK: - SEARCH
@@ -699,6 +1268,10 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - TAP EVENT
+ func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
+ }
+
func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
}
@@ -708,15 +1281,38 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) {
+ if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
NCDownloadAction.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
+ TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""])
+ appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue))
+ NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
+
+ }
+
+ func tapMoreGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ if isEditMode { return }
+ guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
+// toggleMenu(metadata: metadata, image: image)
+ if namedButtonMore == NCGlobal.shared.buttonMoreMore || namedButtonMore == NCGlobal.shared.buttonMoreLock {
+ toggleMenu(metadata: metadata, image: image)
+ } else if namedButtonMore == NCGlobal.shared.buttonMoreStop {
+ Task {
+ await cancelSession(metadata: metadata)
+ }
+ }
}
func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
- toggleMenu(metadata: metadata, image: image, sender: sender)
+// toggleMenu(metadata: metadata, image: image, sender: sender)
+ Task {
+ await cancelSession(metadata: metadata)
+ }
}
+
+ func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func tapRichWorkspace(_ sender: Any) {
if let navigationController = UIStoryboard(name: "NCViewerRichWorkspace", bundle: nil).instantiateInitialViewController() as? UINavigationController {
@@ -742,6 +1338,34 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func tapRecommendations(with metadata: tableMetadata) {
didSelectMetadata(metadata, withOcIds: false)
}
+
+ func tapButtonSwitch(_ sender: Any) {
+ guard !isTransitioning else { return }
+ isTransitioning = true
+
+ guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return }
+
+ if layoutForView.layout == NCGlobal.shared.layoutGrid {
+ layoutForView.layout = NCGlobal.shared.layoutList
+ } else {
+ layoutForView.layout = NCGlobal.shared.layoutGrid
+ }
+ self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
+ self.collectionView.reloadData()
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false }
+ }
+
+ func tapButtonOrder(_ sender: Any) {
+ let sortMenu = NCSortMenu()
+ sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
+ }
+
+ func tapButtonTransfer(_ sender: Any) { }
+
+ func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {
+ }
+ func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
@@ -783,6 +1407,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
+ // MARK: - Transfer Delegate
+
+ func transferProgressDidUpdate(progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String) { }
+
+ func tranferChange(status: String, metadata: tableMetadata, error: NKError) { }
+
// MARK: - Menu Item
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
@@ -828,6 +1458,40 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func getServerData(forced: Bool = false) async { }
+ @objc func reloadDataSource() {
+
+ // get auto upload folder
+ autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
+ autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account)
+
+ // get layout for view
+ layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
+ // set GroupField for Grid
+ if !isSearchingMode && layoutForView?.layout == NCGlobal.shared.layoutGrid {
+ groupByField = "classFile"
+ } else {
+ groupByField = "name"
+ }
+ if isSearchingMode {
+ isDirectoryEncrypted = false
+ } else {
+ isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
+ }
+
+ DispatchQueue.main.async {
+// UIView.transition(with: self.collectionView,
+// duration: 0.20,
+// options: .transitionCrossDissolve,
+// animations: { self.collectionView.reloadData() },
+// completion: nil)
+//
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
+ self.refreshControl.endRefreshing()
+ self.collectionView.reloadData()
+ self.setNavigationRightItems()
+ }
+ }
+
@objc func networkSearch() {
guard !networkSearchInProgress else {
return
@@ -990,18 +1654,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
+ func isHeaderMenuTransferViewEnabled() -> [tableMetadata]? {
+ if headerMenuTransferView,
+ NCNetworking.shared.isOnline,
+ let results = database.getResultsMetadatas(predicate: NSPredicate(format: "status IN %@", [global.metadataStatusWaitUpload, global.metadataStatusUploading])),
+ !results.isEmpty {
+ return Array(results)
+ }
+ return nil
+ }
+
func sizeForHeaderInSection(section: Int) -> CGSize {
var height: CGFloat = 0
let isLandscape = view.bounds.width > view.bounds.height
let isIphone = UIDevice.current.userInterfaceIdiom == .phone
if self.dataSource.isEmpty() {
- height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset)
+ height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil)
} else if isEditMode || (isLandscape && isIphone) {
return CGSize.zero
} else {
- let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: section)
- height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderSection
+ let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section)
+ height = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection
}
return CGSize(width: collectionView.frame.width, height: height)
@@ -1010,25 +1684,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - Footer size
func sizeForFooterInSection(section: Int) -> CGSize {
- guard let controller else {
- return CGSize.zero
- }
let sections = dataSource.numberOfSections()
- let bottomAreaInsets: CGFloat = controller.tabBar.safeAreaInsets.bottom == 0 ? 34 : 0
- let height = controller.tabBar.frame.height + bottomAreaInsets
-
- if isEditMode {
- return CGSize(width: collectionView.frame.width, height: 90 + height)
- }
-
- if isSearchingMode {
- return CGSize(width: collectionView.frame.width, height: 50)
- }
+ let metadataForSection = self.dataSource.getMetadataForSection(section)
+ let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+ let metadatasCount: Int = metadataForSection?.lastSearchResult?.entries.count ?? 0
+ var size = CGSize(width: collectionView.frame.width, height: 0)
if section == sections - 1 {
- return CGSize(width: collectionView.frame.width, height: height)
+ size.height += 85
} else {
- return CGSize(width: collectionView.frame.width, height: 0)
+ size.height += 1
+ }
+
+ if isSearchingMode && isPaginated && metadatasCount > 0 {
+ size.height += 30
}
+ return size
}
}
diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift
index b139b8e303..29fdc89d53 100644
--- a/iOSClient/NCGlobal.swift
+++ b/iOSClient/NCGlobal.swift
@@ -4,18 +4,34 @@
import UIKit
-final class NCGlobal: Sendable {
- static let shared = NCGlobal()
-
- init() { }
-
+/// Used for read/write in Realm
+var isAppSuspending: Bool = false
+/// Used for know if the app in in Background mode
+var isAppInBackground: Bool = false
+
+class NCGlobal: NSObject, @unchecked Sendable {
+ @objc static let shared = NCGlobal()
+
+ override init() {
+ super.init()
+ NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { _ in
+ isAppSuspending = true
+ isAppInBackground = true
+ }
+
+ NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { _ in
+ isAppSuspending = false
+ isAppInBackground = false
+ }
+ }
+
// ENUM
//
public enum TypeFilterScanDocument: String {
case document = "document"
case original = "original"
}
-
+
// Directory on Group
//
let directoryProviderStorage = "File Provider Storage"
@@ -24,13 +40,13 @@ final class NCGlobal: Sendable {
let appDatabaseNextcloud = "Library/Application Support/Nextcloud"
let appScan = "Library/Application Support/Scan"
let appUserData = "Library/Application Support/UserData"
-
+
// Service
//
let metadataKeyedUnarchiver = "it.twsweb.nextcloud.metadata"
let refreshTask = "com.nextcloud.refreshTask"
let processingTask = "com.nextcloud.processingTask"
-
+
// App
//
let appName = "files"
@@ -39,9 +55,10 @@ final class NCGlobal: Sendable {
let spreedName = "spreed"
let twoFactorNotificatioName = "twofactor_nextcloud_notification"
let termsOfServiceName = "terms_of_service"
-
+
// Nextcloud version
//
+ let nextcloudVersion12: Int = 12
let nextcloudVersion18: Int = 18
let nextcloudVersion20: Int = 20
let nextcloudVersion23: Int = 23
@@ -52,21 +69,26 @@ final class NCGlobal: Sendable {
let nextcloudVersion28: Int = 28
let nextcloudVersion30: Int = 30
let nextcloudVersion31: Int = 31
-
+
// Nextcloud unsupported
//
let nextcloud_unsupported_version: Int = 17
-
+
// Intro selector
//
- let introLogin: Int = 0
- let introSignUpWithProvider: Int = 1
-
+ @objc let introLogin: Int = 0
+ let introSignup: Int = 1
+ // let introSignUpWithProvider: Int = 1
+
+ // Varie size GUI
+ //
+ @objc let heightCellSettings: CGFloat = 50
+
// Avatar
//
let avatarSize: Int = 128 * Int(UIScreen.main.scale)
let avatarSizeRounded: Int = 128
-
+
// Preview size
//
let size1024: CGSize = CGSize(width: 1024, height: 1024)
@@ -76,50 +98,50 @@ final class NCGlobal: Sendable {
let previewExt1024 = ".1024.preview.jpg"
let previewExt512 = ".512.preview.jpg"
let previewExt256 = ".256.preview.jpg"
-
+
func getSizeExtension(column: Int) -> String {
if column == 0 { return previewExt256 }
let width = UIScreen.main.bounds.width / CGFloat(column)
-
- switch (width * 4) {
- case 0...384:
- return previewExt256
- case 385...768:
- return previewExt512
- default:
- return previewExt1024
- }
+
+ switch (width * 4) {
+ case 0...384:
+ return previewExt256
+ case 385...768:
+ return previewExt512
+ default:
+ return previewExt1024
+ }
}
-
+
// E2EE
//
let e2eePassphraseTest = "more over television factory tendency independence international intellectual impress interest sentence pony"
- let e2eeVersions = ["1.1", "1.2", "2.0"]
+ @objc let e2eeVersions = ["1.1", "1.2", "2.0"]
let e2eeVersionV11 = "1.1"
let e2eeVersionV12 = "1.2"
let e2eeVersionV20 = "2.0"
-
+
// CHUNK
let chunkSizeMBCellular = 10000000
let chunkSizeMBEthernetOrWiFi = 100000000
-
+
// Video
//
let maxHTTPCache: Int64 = 10000000000 // 10 GB
let fileNameVideoEncoded: String = "video_encoded.mp4"
-
+
// NCViewerProviderContextMenu
//
let maxAutoDownload: UInt64 = 50000000 // 50MB
let maxAutoDownloadCellular: UInt64 = 10000000 // 10MB
-
+
// Layout
//
let layoutList = "typeLayoutList"
let layoutGrid = "typeLayoutGrid"
let layoutPhotoRatio = "typeLayoutPhotoRatio"
let layoutPhotoSquare = "typeLayoutPhotoSquare"
-
+
let layoutViewTrash = "LayoutTrash"
let layoutViewOffline = "LayoutOffline"
let layoutViewFavorite = "LayoutFavorite"
@@ -130,21 +152,52 @@ final class NCGlobal: Sendable {
let layoutViewShareExtension = "LayoutShareExtension"
let layoutViewGroupfolders = "LayoutGroupfolders"
let layoutViewMedia = "LayoutMedia"
+ let layoutViewMove = "LayoutMove"
+
// Button Type in Cell list/grid
//
let buttonMoreMore = "more"
let buttonMoreLock = "moreLock"
-
+ let buttonMoreStop = "stop"
+
+
+ // Standard height sections header/footer
+ //
+ let heightButtonsView: CGFloat = 50
+ let heightHeaderTransfer: CGFloat = 50
+ let heightSection: CGFloat = 30
+ let heightFooter: CGFloat = 1
+ let heightFooterButton: CGFloat = 30
+ let endHeightFooter: CGFloat = 85
+
+
+ // Text - OnlyOffice - Collabora - QuickLook
+ //
+ let editorText = "text"
+ let editorOnlyoffice = "onlyoffice"
+ let editorCollabora = "collabora"
+ let editorQuickLook = "quicklook"
+
+ let onlyofficeDocx = "onlyoffice_docx"
+ let onlyofficeXlsx = "onlyoffice_xlsx"
+ let onlyofficePptx = "onlyoffice_pptx"
+
+ // Template
+ //
+ let templateDocument = "document"
+ let templateSpreadsheet = "spreadsheet"
+ let templatePresentation = "presentation"
+
// Rich Workspace
//
let fileNameRichWorkspace = "Readme.md"
-
+
// ContentPresenter
//
let dismissAfterSecond: TimeInterval = 4
let dismissAfterSecondLong: TimeInterval = 7
-
+
// Error
//
let errorRequestExplicityCancelled: Int = 15
@@ -157,6 +210,7 @@ final class NCGlobal: Sendable {
let errorConflict: Int = 409
let errorPreconditionFailed: Int = 412
let errorUnsupportedMediaType: Int = 415
+ let errorWebDAVLocked: Int = 423
let errorInternalServerError: Int = 500
let errorMaintenance: Int = 503
let errorQuota: Int = 507
@@ -184,6 +238,7 @@ final class NCGlobal: Sendable {
let errorE2EEKeyChecksums: Int = -98002
let errorE2EEKeyEncodeMetadata: Int = -98003
let errorE2EEKeyDecodeMetadataV12: Int = -98004
+ let errorE2EEKeyDecodeMetadata: Int = -98004
let errorE2EEKeyVerifySignature: Int = -98005
let errorE2EEKeyCiphertext: Int = -98006
let errorE2EEKeyFiledropCiphertext: Int = -98007
@@ -208,6 +263,7 @@ final class NCGlobal: Sendable {
let selectorLoadFileQuickLook = "loadFileQuickLook"
let selectorOpenIn = "openIn"
let selectorUploadAutoUpload = "uploadAutoUpload"
+ let selectorUploadAutoUploadAll = "uploadAutoUploadAll"
let selectorUploadFile = "uploadFile"
let selectorUploadFileNODelete = "UploadFileNODelete"
let selectorUploadFileShareExtension = "uploadFileShareExtension"
@@ -215,7 +271,9 @@ final class NCGlobal: Sendable {
let selectorSaveAsScan = "saveAsScan"
let selectorOpenDetail = "openDetail"
let selectorSynchronizationOffline = "synchronizationOffline"
-
+ let selectorPrint = "print"
+ let selectorDeleteFile = "deleteFile"
+
// Metadata : Status
//
// 0 normal
@@ -224,66 +282,88 @@ final class NCGlobal: Sendable {
// ± 3 error
//
let metadataStatusNormal: Int = 0
-
+
let metadataStatusWaitDownload: Int = -1
let metadataStatusDownloading: Int = -2
let metadataStatusDownloadError: Int = -3
-
+
let metadataStatusWaitUpload: Int = 1
let metadataStatusUploading: Int = 2
let metadataStatusUploadError: Int = 3
-
+
let metadataStatusWaitCreateFolder: Int = 10
let metadataStatusWaitDelete: Int = 11
let metadataStatusWaitRename: Int = 12
let metadataStatusWaitFavorite: Int = 13
let metadataStatusWaitCopy: Int = 14
let metadataStatusWaitMove: Int = 15
-
+
let metadataStatusUploadingAllMode = [1,2,3]
let metadataStatusDownloadingAllMode = [-1, -2, -3]
let metadataStatusForScreenAwake = [-1, -2, 1, 2]
+ let metadataStatusInTransfer = [-1, -2, 1, 2]
+ let metadataStatusFileDown = [-1, -2, -3]
let metadataStatusHideInView = [1, 2, 3, 11]
+ let metadataStatusHideInFileExtension = [1, 2, 3, 10, 11]
let metadataStatusWaitWebDav = [10, 11, 12, 13, 14, 15]
let metadataStatusTransfers = [-2, -3, 2, 3, 10, 11, 12, 13, 14, 15]
+ let metadataStatusObserveNetworkingProcess = [-1, 1, 10, 11, 12, 13, 14, 15]
+ let metadataStatusObserveTrasfers = [-2, 2, 10, 11, 12, 13, 14, 15]
+ // Hidden files included in the read
+ //
+ let includeHiddenFiles: [String] = [".LivePhoto"]
+
// Auto upload subfolder granularity
//
- let subfolderGranularityDaily = 2
- let subfolderGranularityMonthly = 1
- let subfolderGranularityYearly = 0
-
+ @objc let subfolderGranularityDaily = 2
+ @objc let subfolderGranularityMonthly = 1
+ @objc let subfolderGranularityYearly = 0
+
// Notification Center
//
- let notificationCenterChangeUser = "changeUser" // userInfo: account, controller
- let notificationCenterChangeTheming = "changeTheming" // userInfo: account
+ @objc let notificationCenterChangeUser = "changeUser"
+ let notificationCenterChangeTheming = "changeTheming"
+ @objc let notificationCenterApplicationDidEnterBackground = "applicationDidEnterBackground"
+ @objc let notificationCenterApplicationDidBecomeActive = "applicationDidBecomeActive"
+ @objc let notificationCenterApplicationWillResignActive = "applicationWillResignActive"
+ @objc let notificationCenterApplicationWillEnterForeground = "applicationWillEnterForeground"
+
+
+ @objc let notificationCenterInitialize = "initialize"
let notificationCenterRichdocumentGrabFocus = "richdocumentGrabFocus"
let notificationCenterReloadDataNCShare = "reloadDataNCShare"
+ let notificationCenterDidCreateShareLink = "didCreateShareLink"
+
let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView"
let notificationCenterReloadAvatar = "reloadAvatar"
+ let notificationCenterReloadHeader = "reloadHeader"
let notificationCenterClearCache = "clearCache"
+ let notificationCenterChangeLayout = "changeLayout" // userInfo: account, serverUrl, layoutForView
let notificationCenterCheckUserDelaultErrorDone = "checkUserDelaultErrorDone" // userInfo: account, controller
let notificationCenterServerDidUpdate = "serverDidUpdate" // userInfo: account
let notificationCenterNetworkReachability = "networkReachability"
+ let notificationCenterCreateMediaCacheEnded = "createMediaCacheEnded"
let notificationCenterUpdateNotification = "updateNotification"
+
let notificationCenterReloadDataSource = "reloadDataSource" // userInfo: serverUrl?, clearDataSource
let notificationCenterGetServerData = "getServerData" // userInfo: serverUrl?
-
+
let notificationCenterChangeStatusFolderE2EE = "changeStatusFolderE2EE" // userInfo: serverUrl
-
+
let notificationCenterDownloadStartFile = "downloadStartFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
let notificationCenterDownloadedFile = "downloadedFile" // userInfo: ocId, ocIdTransfer, session, session, serverUrl, account, selector, error
let notificationCenterDownloadCancelFile = "downloadCancelFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
-
+
let notificationCenterUploadStartFile = "uploadStartFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, sessionSelector
let notificationCenterUploadedFile = "uploadedFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, ocIdTransfer, error
let notificationCenterUploadedLivePhoto = "uploadedLivePhoto" // userInfo: ocId, ocIdTransfer, session, serverUrl, account, fileName, ocIdTransfer, error
let notificationCenterUploadCancelFile = "uploadCancelFile" // userInfo: ocId, ocIdTransfer, session, serverUrl, account
-
+
let notificationCenterProgressTask = "progressTask" // userInfo: account, ocId, ocIdTransfer, session, serverUrl, status, chunk, e2eEncrypted, progress, totalBytes, totalBytesExpected
-
+
let notificationCenterUpdateBadgeNumber = "updateBadgeNumber" // userInfo: counterDownload, counterUpload
-
+
let notificationCenterCreateFolder = "createFolder" // userInfo: ocId, serverUrl, account, withPush, sceneIdentifier
let notificationCenterDeleteFile = "deleteFile" // userInfo: [ocId], error
let notificationCenterCopyMoveFile = "copyMoveFile" // userInfo: [ocId] serverUrl, account, dragdrop, type (copy, move)
@@ -292,20 +372,32 @@ final class NCGlobal: Sendable {
let notificationCenterRenameFile = "renameFile" // userInfo: serverUrl, account, error
let notificationCenterFavoriteFile = "favoriteFile" // userInfo: ocId, serverUrl
let notificationCenterFileExists = "fileExists" // userInfo: ocId, fileExists
-
+
let notificationCenterMenuSearchTextPDF = "menuSearchTextPDF"
let notificationCenterMenuGotToPageInPDF = "menuGotToPageInPDF"
-
+
let notificationCenterOpenMediaDetail = "openMediaDetail" // userInfo: ocId
-
+
let notificationCenterDismissScanDocument = "dismissScanDocument"
let notificationCenterDismissUploadAssets = "dismissUploadAssets"
-
+
let notificationCenterEnableSwipeGesture = "enableSwipeGesture"
let notificationCenterDisableSwipeGesture = "disableSwipeGesture"
-
+
+ let notificationCenterShareViewIn = "ShareViewIn"
+ let notificationCenterShareAdvancePermission = "ShareAdvancePermission"
+ let notificationCenterShareSendEmail = "ShareSendEmail"
+ let notificationCenterShareUnshare = "ShareUnshare"
+ let notificationCenterStatusReadOnly = "statusReadOnly"
+ let notificationCenterStatusEditing = "statusEditing"
+ let notificationCenterStatusFileDrop = "statusFileDrop"
+
let notificationCenterPlayerIsPlaying = "playerIsPlaying"
let notificationCenterPlayerStoppedPlaying = "playerStoppedPlaying"
+
+ let notificationCenterUpdateShare = "updateShare"
+ let notificationCenterShareCountsUpdated = "shareCountsUpdated"
+ let notificationCenterUpdateIcons = "updateIcons"
// Networking Status
let networkingStatusCreateFolder = "statusCreateFolder"
@@ -332,6 +424,7 @@ final class NCGlobal: Sendable {
let tipScanAddImage = "tipScanAddImage"
let tipMediaDetailView = "tipMediaDetailView"
let tipAutoUploadButton = "tipAutoUploadButton"
+ let tipAutoUpload = "tipAutoUpload"
// ACTION
//
@@ -362,10 +455,31 @@ final class NCGlobal: Sendable {
let configuration_disable_multiaccount = "disable_multiaccount"
let configuration_disable_crash_service = "disable_crash_service"
let configuration_disable_log = "disable_log"
+ let configuration_disable_manage_account = "disable_manage_account"
let configuration_disable_more_external_site = "disable_more_external_site"
let configuration_disable_openin_file = "disable_openin_file"
let configuration_enforce_passcode_lock = "enforce_passcode_lock"
-
+
+ // CAPABILITIES
+ //
+ var capabilityServerVersionMajor: Int = 0
+ @objc var capabilityServerVersion: String = ""
+ @objc var capabilityThemingName: String = ""
+ @objc var capabilityThemingSlogan: String = ""
+
+ @objc var capabilityE2EEEnabled: Bool = false
+ @objc var capabilityE2EEApiVersion: String = ""
+
+ var capabilityRichdocumentsEnabled: Bool = false
+ var capabilityRichdocumentsMimetypes: [String] = []
+ var capabilityActivity: [String] = []
+ var capabilityNotification: [String] = []
+
+ @objc var capabilityUserStatusEnabled: Bool = false
+ var isLivePhotoServerAvailable: Bool { // NC28
+ return capabilityServerVersionMajor >= nextcloudVersion28
+ }
+
// MORE NEXTCLOUD APPS
//
let talkSchemeUrl = "nextcloudtalk://"
@@ -373,40 +487,59 @@ final class NCGlobal: Sendable {
let talkAppStoreUrl = "https://apps.apple.com/in/app/nextcloud-talk/id1296825574"
let notesAppStoreUrl = "https://apps.apple.com/in/app/nextcloud-notes/id813973264"
let moreAppsUrl = "itms-apps://search.itunes.apple.com/WebObjects/MZSearch.woa/wa/search?media=software&term=nextcloud"
-
+
// SNAPSHOT PREVIEW
//
let defaultSnapshotConfiguration = "DefaultPreviewConfiguration"
-
+
+// // FORBIDDEN CHARACTERS
+// //
+// // TODO: Remove this
+// let forbiddenCharacters = ["/", "\\", ":", "\"", "|", "?", "*", "<", ">"]
+
// DIAGNOSTICS CLIENTS
//
let diagnosticIssueSyncConflicts = "sync_conflicts"
let diagnosticIssueProblems = "problems"
let diagnosticIssueVirusDetected = "virus_detected"
let diagnosticIssueE2eeErrors = "e2ee_errors"
-
+
let diagnosticProblemsForbidden = "CHARACTERS_FORBIDDEN"
let diagnosticProblemsBadResponse = "BAD_SERVER_RESPONSE"
let diagnosticProblemsUploadServerError = "UploadError.SERVER_ERROR"
-
+
// MEDIA LAYOUT
//
let mediaLayoutRatio = "mediaLayoutRatio"
let mediaLayoutSquare = "mediaLayoutSquare"
-
+
// DRAG & DROP
//
let metadataOcIdDataRepresentation = "text/com.nextcloud.ocId"
-
+
// GROUP AMIN
//
let groupAdmin = "admin"
- // TASK DESCRIPTION
+ // DATA TASK DESCRIPTION
//
let taskDescriptionRetrievesProperties = "retrievesProperties"
let taskDescriptionSynchronization = "synchronization"
-
+ let taskDescriptionDeleteFileOrFolder = "deleteFileOrFolder"
+
+ // MoEngage App Version
+ //
+ let moEngageAppVersion = 854
+
+ // Filename Mask and Type
+ //
+ let keyFileNameMask = "fileNameMask"
+ let keyFileNameType = "fileNameType"
+ let keyFileNameAutoUploadMask = "fileNameAutoUploadMask"
+ let keyFileNameAutoUploadType = "fileNameAutoUploadType"
+ let keyFileNameOriginal = "fileNameOriginal"
+ let keyFileNameOriginalAutoUpload = "fileNameOriginalAutoUpload"
+
// LOG TAG
//
let logTagTask = "BGT"
From 692e7526cc3ec21750860bee1604e4b894bb6a02 Mon Sep 17 00:00:00 2001
From: Amrut Waghmare
Date: Tue, 20 Jun 2023 16:56:45 +0530
Subject: [PATCH 029/177] nmc 2298 - Share extension theaming changes
---
Share/NCShareExtension.swift | 41 ++++++++----------------------------
1 file changed, 9 insertions(+), 32 deletions(-)
diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift
index f198a10231..d72783be51 100644
--- a/Share/NCShareExtension.swift
+++ b/Share/NCShareExtension.swift
@@ -77,8 +77,8 @@ class NCShareExtension: UIViewController {
uploadView.layer.cornerRadius = 10
uploadLabel.text = NSLocalizedString("_upload_", comment: "")
- uploadLabel.textColor = .systemBlue
- let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload(_:)))
+ uploadLabel.textColor = NCBrandColor.shared.customer
+ let uploadGesture = UITapGestureRecognizer(target: self, action: #selector(actionUpload))
uploadView.addGestureRecognizer(uploadGesture)
let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionBuild())
@@ -190,14 +190,15 @@ class NCShareExtension: UIViewController {
navigationItem.title = navigationTitle
cancelButton.title = NSLocalizedString("_cancel_", comment: "")
+ cancelButton.tintColor = NCBrandColor.shared.customer
// BACK BUTTON
let backButton = UIButton(type: .custom)
- backButton.setImage(UIImage(named: "back"), for: .normal)
- backButton.tintColor = .systemBlue
+ backButton.setImage(UIImage(named: "back")?.imageColor(NCBrandColor.shared.customer), for: .normal)
+ backButton.tintColor = NCBrandColor.shared.customer
backButton.semanticContentAttribute = .forceLeftToRight
backButton.setTitle(" " + NSLocalizedString("_back_", comment: ""), for: .normal)
- backButton.setTitleColor(.systemBlue, for: .normal)
+ backButton.setTitleColor(NCBrandColor.shared.customer, for: .normal)
backButton.action(for: .touchUpInside) { _ in
while self.serverUrl.last != "/" { self.serverUrl.removeLast() }
self.serverUrl.removeLast()
@@ -210,35 +211,11 @@ class NCShareExtension: UIViewController {
}
self.setNavigationBar(navigationTitle: navigationTitle)
}
-
- let image = utility.loadUserImage(for: tblAccount.user, displayName: tblAccount.displayName, urlBase: tblAccount.urlBase)
- let profileButton = UIButton(type: .custom)
- profileButton.setImage(image, for: .normal)
-
- if serverUrl == utilityFileSystem.getHomeServer(session: session) {
- var title = " "
- if !tblAccount.alias.isEmpty {
- title += tblAccount.alias
- } else {
- title += tblAccount.displayName
- }
-
- profileButton.setTitle(title, for: .normal)
- profileButton.setTitleColor(.systemBlue, for: .normal)
- }
-
- profileButton.semanticContentAttribute = .forceLeftToRight
- profileButton.sizeToFit()
- profileButton.action(for: .touchUpInside) { _ in
- self.showAccountPicker()
- }
- var navItems = [UIBarButtonItem(customView: profileButton)]
if serverUrl != utilityFileSystem.getHomeServer(session: session) {
- let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
- space.width = 20
- navItems.append(contentsOf: [UIBarButtonItem(customView: backButton), space])
+ navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
+ } else {
+ navigationItem.leftBarButtonItem = nil
}
- navigationItem.setLeftBarButtonItems(navItems, animated: true)
}
func setCommandView() {
From 9065b77239da5702ee61df39175d0a3d14c10729 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 15 Apr 2025 17:42:32 +0530
Subject: [PATCH 030/177] NMC 2298 - Share Extension theming changes, added
emptydataset changes
---
Share/NCShareExtension.swift | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift
index d72783be51..3894d499b6 100644
--- a/Share/NCShareExtension.swift
+++ b/Share/NCShareExtension.swift
@@ -31,6 +31,7 @@ class NCShareExtension: UIViewController {
var filesName: [String] = []
// -------------------------------------------------------------
+ var emptyDataSet: NCEmptyDataSet?
let keyLayout = NCGlobal.shared.layoutViewShareExtension
var metadataFolder: tableMetadata?
var dataSourceTask: URLSessionTask?
@@ -53,9 +54,13 @@ class NCShareExtension: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
+ self.navigationController?.navigationBar.prefersLargeTitles = false
collectionView.register(UINib(nibName: "NCListCell", bundle: nil), forCellWithReuseIdentifier: "listCell")
collectionView.collectionViewLayout = NCListLayout()
+ collectionView.refreshControl = refreshControl
+ refreshControl.tintColor = NCBrandColor.shared.brandText
+ refreshControl.backgroundColor = .systemBackground
+ refreshControl.addTarget(self, action: #selector(reloadDatasource), for: .valueChanged)
commandView.backgroundColor = .secondarySystemBackground
separatorView.backgroundColor = .separator
@@ -69,7 +74,7 @@ class NCShareExtension: UIViewController {
cancelButton.title = NSLocalizedString("_cancel_", comment: "")
createFolderView.layer.cornerRadius = 10
- createFolderImage.image = utility.loadImage(named: "folder.badge.plus", colors: [NCBrandColor.shared.iconImageColor])
+ createFolderImage.image = utility.loadImage(named: "folder.badge.plus", colors: [NCBrandColor.shared.iconColor])
createFolderLabel.text = NSLocalizedString("_create_folder_", comment: "")
let createFolderGesture = UITapGestureRecognizer(target: self, action: #selector(actionCreateFolder(_:)))
createFolderView.addGestureRecognizer(createFolderGesture)
@@ -109,6 +114,9 @@ class NCShareExtension: UIViewController {
if let account = NCShareExtensionData.shared.getTblAccoun()?.account {
accountRequestChangeAccount(account: account, controller: nil)
}
+ NCImageCache.shared.createImagesCache()
+
+ NotificationCenter.default.addObserver(self, selector: #selector(didCreateFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
@@ -230,6 +238,10 @@ class NCShareExtension: UIViewController {
self.tableView.isScrollEnabled = false
}
uploadLabel.text = NSLocalizedString("_upload_", comment: "") + " \(filesName.count) " + NSLocalizedString("_files_", comment: "")
+
+ // Empty
+ emptyDataSet = NCEmptyDataSet(view: collectionView, offset: -50 * counter, delegate: self)
+
self.tableView.reloadData()
}
From 31abd7233d6dd67405e4dbf482d42e70e259cfeb Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 18:43:58 +0530
Subject: [PATCH 031/177] NMC-2298 - Share extension theming changes
---
Share/NCShareExtension.swift | 63 ++++++++++++++++++++++++++++++++++--
1 file changed, 61 insertions(+), 2 deletions(-)
diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift
index 3894d499b6..2a423690dd 100644
--- a/Share/NCShareExtension.swift
+++ b/Share/NCShareExtension.swift
@@ -10,7 +10,7 @@ enum NCShareExtensionError: Error {
case cancel, fileUpload, noAccount, noFiles, versionMismatch
}
-class NCShareExtension: UIViewController {
+class NCShareExtension: UIViewController, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var cancelButton: UIBarButtonItem!
@@ -40,15 +40,31 @@ class NCShareExtension: UIViewController {
let heightCommandView: CGFloat = 170
var autoUploadFileName = ""
var autoUploadDirectory = ""
+ let refreshControl = UIRefreshControl()
var progress: CGFloat = 0
var counterUploaded: Int = 0
+ var uploadErrors: [tableMetadata] = []
var uploadMetadata: [tableMetadata] = []
+ var uploadStarted = false
let hud = NCHud()
let utilityFileSystem = NCUtilityFileSystem()
let utility = NCUtility()
let global = NCGlobal.shared
var maintenanceMode: Bool = false
-
+ let database = NCManageDatabase.shared
+ var account: String = ""
+ var session: NCSession.Session {
+ if !account.isEmpty,
+ let tableAccount = self.database.getTableAccount(account: account) {
+ return NCSession.Session(account: tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId)
+ } else if let activeTableAccount = self.database.getActiveTableAccount() {
+ self.account = activeTableAccount.account
+ return NCSession.Session(account: activeTableAccount.account, urlBase: activeTableAccount.urlBase, user: activeTableAccount.user, userId: activeTableAccount.userId)
+ } else {
+ return NCSession.Session(account: "", urlBase: "", user: "", userId: "")
+ }
+ }
+
// MARK: - View Life Cycle
override func viewDidLoad() {
@@ -91,6 +107,21 @@ class NCShareExtension: UIViewController {
nkLog(start: "Start Share session " + versionNextcloudiOS)
+ // LOG
+ let levelLog = NCKeychain().logLevel
+//
+// NextcloudKit.shared.nkCommonInstance.levelLog = levelLog
+// NextcloudKit.shared.nkCommonInstance.pathLog = utilityFileSystem.directoryGroup
+// NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start Share session with level \(levelLog) " + versionNextcloudiOS)
+ NKLogFileManager.shared.logLevel = NKLogLevel(rawValue: levelLog) ?? .normal
+ NKLogFileManager.shared.logDirectory = URL(fileURLWithPath: utilityFileSystem.directoryGroup)
+ NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start Share session with level \(levelLog) " + versionNextcloudiOS)
+
+// hud.indicatorView = JGProgressHUDRingIndicatorView()
+// if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
+// indicatorView.ringWidth = 1.5
+// indicatorView.ringColor = NCBrandColor.shared.brandElement
+// }
NCBrandColor.shared.createUserColors()
NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { _ in
@@ -136,6 +167,8 @@ class NCShareExtension: UIViewController {
}
}
+ accountRequestChangeAccount(account: account, controller: nil)
+
guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else {
cancel(with: .noFiles)
return
@@ -174,10 +207,26 @@ class NCShareExtension: UIViewController {
}
}
+ // MARK: - Empty
+
+ func emptyDataSetView(_ view: NCEmptyView) {
+
+ if self.dataSourceTask?.state == .running {
+ view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+ view.emptyDescription.text = ""
+ } else {
+ view.emptyImage.image = UIImage(named: "folder_nmcloud")
+ view.emptyTitle.text = NSLocalizedString("_files_no_folders_", comment: "")
+ view.emptyDescription.text = ""
+ }
+ }
+
// MARK: -
func cancel(with error: NCShareExtensionError) {
// make sure no uploads are continued
+ uploadStarted = false
extensionContext?.cancelRequest(withError: error)
}
@@ -439,6 +488,16 @@ extension NCShareExtension {
}
}
+extension NCShareExtension: uploadE2EEDelegate {
+ func start() {
+ self.hud.progress(0)
+ }
+
+ func uploadE2EEProgress(_ totalBytesExpected: Int64, _ totalBytes: Int64, _ fractionCompleted: Double) {
+ self.hud.progress(fractionCompleted)
+ }
+}
+
extension NCShareExtension: NCPasscodeDelegate {
func passcodeReset(_ passcodeViewController: TOPasscodeViewController) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
From 5081c05a381e4cd986b89674e050b5c7e339ba8b Mon Sep 17 00:00:00 2001
From: Shweta Waikar
Date: Wed, 28 Jun 2023 12:11:45 +0530
Subject: [PATCH 032/177] NMC 2341 - Theming changes
---
iOSClient/NCGlobal.swift | 1 +
iOSClient/Trash/Cell/NCTrashGridCell.swift | 146 ++++++-------
iOSClient/Trash/Cell/NCTrashGridCell.xib | 214 +++++++++----------
iOSClient/Trash/Cell/NCTrashListCell.swift | 8 +-
iOSClient/Trash/NCTrash+CollectionView.swift | 51 +++--
iOSClient/Trash/NCTrash.swift | 17 +-
6 files changed, 223 insertions(+), 214 deletions(-)
diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift
index 4695fc5c36..f11beabf87 100644
--- a/iOSClient/NCGlobal.swift
+++ b/iOSClient/NCGlobal.swift
@@ -135,6 +135,7 @@ final class NCGlobal: Sendable {
//
let buttonMoreMore = "more"
let buttonMoreLock = "moreLock"
+ let buttonMoreStop = "stop"
// Rich Workspace
//
diff --git a/iOSClient/Trash/Cell/NCTrashGridCell.swift b/iOSClient/Trash/Cell/NCTrashGridCell.swift
index 0fae0620f7..62385894db 100644
--- a/iOSClient/Trash/Cell/NCTrashGridCell.swift
+++ b/iOSClient/Trash/Cell/NCTrashGridCell.swift
@@ -2,23 +2,8 @@
// NCTrashGridCell.swift
// Nextcloud
//
-// Created by Marino Faggiana on 19/03/2024.
-// Copyright © 2024 Marino Faggiana. All rights reserved.
-//
-// Author Marino Faggiana
-//
-// 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
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// Created by A200073704 on 27/06/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
//
import UIKit
@@ -29,20 +14,67 @@ protocol NCTrashGridCellDelegate: AnyObject {
class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
+
@IBOutlet weak var imageItem: UIImageView!
@IBOutlet weak var imageSelect: UIImageView!
+ @IBOutlet weak var imageStatus: UIImageView!
+ @IBOutlet weak var imageFavorite: UIImageView!
+ @IBOutlet weak var imageLocal: UIImageView!
@IBOutlet weak var labelTitle: UILabel!
@IBOutlet weak var labelInfo: UILabel!
- @IBOutlet weak var labelSubinfo: UILabel!
@IBOutlet weak var buttonMore: UIButton!
@IBOutlet weak var imageVisualEffect: UIVisualEffectView!
+ @IBOutlet weak var progressView: UIProgressView!
- weak var delegate: NCTrashGridCellDelegate?
- var objectId = ""
+ internal var objectId = ""
var indexPath = IndexPath()
var account = ""
var user = ""
+ weak var delegate: NCTrashGridCellDelegate?
+ var namedButtonMore = ""
+
+ var fileObjectId: String? {
+ get { return objectId }
+ set { objectId = newValue ?? "" }
+ }
+ var filePreviewImageView: UIImageView? {
+ get { return imageItem }
+ set { imageItem = newValue }
+ }
+ var fileUser: String? {
+ get { return user }
+ set { user = newValue ?? "" }
+ }
+ var fileTitleLabel: UILabel? {
+ get { return labelTitle }
+ set { labelTitle = newValue }
+ }
+ var fileInfoLabel: UILabel? {
+ get { return labelInfo }
+ set { labelInfo = newValue }
+ }
+ var fileProgressView: UIProgressView? {
+ get { return progressView }
+ set { progressView = newValue }
+ }
+ var fileSelectImage: UIImageView? {
+ get { return imageSelect }
+ set { imageSelect = newValue }
+ }
+ var fileStatusImage: UIImageView? {
+ get { return imageStatus }
+ set { imageStatus = newValue }
+ }
+ var fileLocalImage: UIImageView? {
+ get { return imageLocal }
+ set { imageLocal = newValue }
+ }
+ var fileFavoriteImage: UIImageView? {
+ get { return imageFavorite }
+ set { imageFavorite = newValue }
+ }
+
override func awakeFromNib() {
super.awakeFromNib()
initCell()
@@ -66,9 +98,14 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
imageVisualEffect.clipsToBounds = true
imageVisualEffect.alpha = 0.5
+ progressView.tintColor = NCBrandColor.shared.brandElement
+ progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5)
+ progressView.trackTintColor = .clear
+
labelTitle.text = ""
labelInfo.text = ""
- labelSubinfo.text = ""
+ labelTitle.textColor = .label
+ labelInfo.textColor = .systemGray
}
override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
@@ -79,7 +116,10 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
delegate?.tapMoreGridItem(with: objectId, image: imageItem.image, sender: sender)
}
+
fileprivate func setA11yActions() {
+ let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_"
+
self.accessibilityCustomActions = [
UIAccessibilityCustomAction(
name: NSLocalizedString("_more_", comment: ""),
@@ -88,7 +128,8 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
]
}
- func setButtonMore(image: UIImage) {
+ func setButtonMore(named: String, image: UIImage) {
+ namedButtonMore = named
buttonMore.setImage(image, for: .normal)
setA11yActions()
}
@@ -110,7 +151,7 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
imageSelect.isHidden = false
imageVisualEffect.isHidden = false
} else {
- imageSelect.isHidden = true
+ imageSelect.image = NCImageCache.shared.getImageCheckedNo()
imageVisualEffect.isHidden = true
}
}
@@ -121,8 +162,7 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale.current
- labelInfo.text = dateFormatter.string(from: date as Date)
- labelSubinfo.text = NCUtilityFileSystem().transformedSize(size)
+ labelInfo.text = dateFormatter.string(from: date as Date) + " · " + NCUtilityFileSystem().transformedSize(size)
}
func setAccessibility(label: String, value: String) {
@@ -130,59 +170,3 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
accessibilityValue = value
}
}
-
-// MARK: - Grid Layout
-
-class NCTrashGridLayout: UICollectionViewFlowLayout {
-
- var heightLabelPlusButton: CGFloat = 60
- var marginLeftRight: CGFloat = 10
- var itemForLine: CGFloat = 3
- var itemWidthDefault: CGFloat = 140
-
- // MARK: - View Life Cycle
-
- override init() {
- super.init()
-
- sectionHeadersPinToVisibleBounds = false
-
- minimumInteritemSpacing = 1
- minimumLineSpacing = marginLeftRight
-
- self.scrollDirection = .vertical
- self.sectionInset = UIEdgeInsets(top: 10, left: marginLeftRight, bottom: 0, right: marginLeftRight)
- }
-
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override var itemSize: CGSize {
- get {
- if let collectionView = collectionView {
-
- if collectionView.frame.width < 400 {
- itemForLine = 3
- } else {
- itemForLine = collectionView.frame.width / itemWidthDefault
- }
-
- let itemWidth: CGFloat = (collectionView.frame.width - marginLeftRight * 2 - marginLeftRight * (itemForLine - 1)) / itemForLine
- let itemHeight: CGFloat = itemWidth + heightLabelPlusButton
-
- return CGSize(width: itemWidth, height: itemHeight)
- }
-
- // Default fallback
- return CGSize(width: itemWidthDefault, height: itemWidthDefault)
- }
- set {
- super.itemSize = newValue
- }
- }
-
- override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
- return proposedContentOffset
- }
-}
diff --git a/iOSClient/Trash/Cell/NCTrashGridCell.xib b/iOSClient/Trash/Cell/NCTrashGridCell.xib
index 607f2744c6..91bd5f805d 100644
--- a/iOSClient/Trash/Cell/NCTrashGridCell.xib
+++ b/iOSClient/Trash/Cell/NCTrashGridCell.xib
@@ -1,9 +1,9 @@
-
-
+
+
-
+
@@ -11,140 +11,130 @@
-
-
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
-
-
-
-
-
-
-
+
diff --git a/iOSClient/Trash/Cell/NCTrashListCell.swift b/iOSClient/Trash/Cell/NCTrashListCell.swift
index 5bd89e1615..d2df55c700 100644
--- a/iOSClient/Trash/Cell/NCTrashListCell.swift
+++ b/iOSClient/Trash/Cell/NCTrashListCell.swift
@@ -46,6 +46,8 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol {
weak var delegate: NCTrashListCellDelegate?
var objectId = ""
var account = ""
+ var indexPath = IndexPath()
+ let utility = NCUtility()
override func awakeFromNib() {
super.awakeFromNib()
@@ -72,8 +74,10 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol {
]
- imageRestore.image = NCUtility().loadImage(named: "arrow.counterclockwise", colors: [NCBrandColor.shared.iconImageColor])
- imageMore.image = NCUtility().loadImage(named: "trash", colors: [.red])
+ imageRestore.image = utility.loadImage(named: "restore", color: NCBrandColor.shared.iconColor)
+ imageMore.image = UIImage(systemName: "trash")
+ imageMore.tintColor = NCBrandColor.shared.iconColor
+
imageItem.layer.cornerRadius = 6
imageItem.layer.masksToBounds = true
diff --git a/iOSClient/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift
index 8995ce177c..94a2df94be 100644
--- a/iOSClient/Trash/NCTrash+CollectionView.swift
+++ b/iOSClient/Trash/NCTrash+CollectionView.swift
@@ -23,9 +23,11 @@
import UIKit
import RealmSwift
+import Foundation
// MARK: UICollectionViewDelegate
extension NCTrash: UICollectionViewDelegate {
+
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let resultTableTrash = datasource?[indexPath.item] else { return }
guard !isEditMode else {
@@ -35,7 +37,7 @@ extension NCTrash: UICollectionViewDelegate {
selectOcId.append(resultTableTrash.fileId)
}
collectionView.reloadItems(at: [indexPath])
- tabBarSelect.update(selectOcId: selectOcId)
+ setNavigationRightItems()
return
}
@@ -51,7 +53,9 @@ extension NCTrash: UICollectionViewDelegate {
// MARK: UICollectionViewDataSource
extension NCTrash: UICollectionViewDataSource {
+
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ setNavigationRightItems()
return datasource?.count ?? 0
}
@@ -65,7 +69,7 @@ extension NCTrash: UICollectionViewDataSource {
cell = listCell
} else {
let gridCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCTrashGridCell)!
- gridCell.setButtonMore(image: NCImageCache.shared.getImageButtonMore())
+ gridCell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.shared.getImageButtonMore())
gridCell.delegate = self
cell = gridCell
}
@@ -93,12 +97,10 @@ extension NCTrash: UICollectionViewDataSource {
}
}
}
-
cell.account = resultTableTrash.account
cell.objectId = resultTableTrash.fileId
cell.setupCellUI(tableTrash: resultTableTrash, image: image)
cell.selected(selectOcId.contains(resultTableTrash.fileId), isEditMode: isEditMode, account: resultTableTrash.account)
-
return cell
}
@@ -142,18 +144,34 @@ extension NCTrash: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
- guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData
- else { return NCSectionFirstHeaderEmptyData() }
- header.emptyImage.image = utility.loadImage(named: "trash", colors: [NCBrandColor.shared.getElement(account: session.account)])
- header.emptyTitle.text = NSLocalizedString("_trash_no_trash_", comment: "")
- header.emptyDescription.text = NSLocalizedString("_trash_no_trash_description_", comment: "")
+
+ guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCSectionHeaderMenu
+ else { return UICollectionReusableView() }
+
+ if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+ header.setImageSwitchList()
+ header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
+ } else {
+ header.setImageSwitchGrid()
+ header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
+ }
+
+ header.delegate = self
+ header.setStatusButtonsView(enable: !(datasource?.isEmpty ?? false))
+ header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
+ header.setButtonsView(height: NCGlobal.shared.heightButtonsView)
+ header.setRichWorkspaceHeight(0)
+ header.setSectionHeight(0)
+ header.setViewTransfer(isHidden: true)
+
return header
+
} else {
guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter
- else { return NCSectionFooter() }
- if let datasource {
- footer.setTitleLabel(setTextFooter(datasource: datasource))
- }
+ else { return UICollectionReusableView() }
+ guard let datasource else { return footer }
+ footer.setTitleLabel(setTextFooter(datasource: datasource))
+ footer.separatorIsHidden(true)
return footer
}
}
@@ -161,12 +179,9 @@ extension NCTrash: UICollectionViewDataSource {
// MARK: UICollectionViewDelegateFlowLayout
extension NCTrash: UICollectionViewDelegateFlowLayout {
+
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
- var height: Double = 0
- if let datasource, datasource.isEmpty {
- height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: 0)
- }
- return CGSize(width: collectionView.frame.width, height: height)
+ return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightButtonsView)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 85)
diff --git a/iOSClient/Trash/NCTrash.swift b/iOSClient/Trash/NCTrash.swift
index 797fba1e7c..9d588303a9 100644
--- a/iOSClient/Trash/NCTrash.swift
+++ b/iOSClient/Trash/NCTrash.swift
@@ -27,7 +27,7 @@ import UIKit
import NextcloudKit
import RealmSwift
-class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegate {
+class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegate, NCSectionHeaderMenuDelegate {
@IBOutlet weak var collectionView: UICollectionView!
var filePath = ""
@@ -68,6 +68,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
collectionView.register(UINib(nibName: "NCTrashGridCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
+ collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
collectionView.alwaysBounceVertical = true
@@ -158,6 +159,20 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
}
}
+ func tapButtonSwitch(_ sender: Any) {
+ if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+ onListSelected()
+ } else {
+ onGridSelected()
+ }
+ }
+
+ func tapButtonOrder(_ sender: Any) {
+
+ let sortMenu = NCSortMenu()
+ sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
+ }
+
func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressMoreGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { }
From 1cbc5f42eec01b526fba9058cd0ce680caa18f86 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Mon, 14 Apr 2025 16:57:15 +0530
Subject: [PATCH 033/177] NMC 2341 - NCTrashView changes
---
iOSClient/Trash/Cell/NCTrashGridCell.swift | 11 ++-
iOSClient/Trash/Cell/NCTrashListCell.swift | 10 ++-
iOSClient/Trash/NCTrash+CollectionView.swift | 28 ++++---
iOSClient/Trash/NCTrash.swift | 78 +++++++++++++++++---
4 files changed, 100 insertions(+), 27 deletions(-)
diff --git a/iOSClient/Trash/Cell/NCTrashGridCell.swift b/iOSClient/Trash/Cell/NCTrashGridCell.swift
index 62385894db..85ee0fd190 100644
--- a/iOSClient/Trash/Cell/NCTrashGridCell.swift
+++ b/iOSClient/Trash/Cell/NCTrashGridCell.swift
@@ -14,7 +14,6 @@ protocol NCTrashGridCellDelegate: AnyObject {
class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
-
@IBOutlet weak var imageItem: UIImageView!
@IBOutlet weak var imageSelect: UIImageView!
@IBOutlet weak var imageStatus: UIImageView!
@@ -122,7 +121,7 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
self.accessibilityCustomActions = [
UIAccessibilityCustomAction(
- name: NSLocalizedString("_more_", comment: ""),
+ name: NSLocalizedString(moreName, comment: ""),
target: self,
selector: #selector(touchUpInsideMore(_:)))
]
@@ -140,19 +139,23 @@ class NCTrashGridCell: UICollectionViewCell, NCTrashCellProtocol {
func selected(_ status: Bool, isEditMode: Bool, account: String) {
if isEditMode {
+ imageSelect.isHidden = false
buttonMore.isHidden = true
accessibilityCustomActions = nil
} else {
+ imageSelect.isHidden = true
buttonMore.isHidden = false
+ imageVisualEffect.isHidden = true
setA11yActions()
}
if status {
+ let traitCollectionUserInterfaceStyleDark = traitCollection.userInterfaceStyle == .dark
+ imageVisualEffect.effect = UIBlurEffect(style: traitCollectionUserInterfaceStyleDark ? .dark : .extraLight)
+ imageVisualEffect.backgroundColor = traitCollectionUserInterfaceStyleDark ? .black : .lightGray
imageSelect.image = NCImageCache.shared.getImageCheckedYes()
- imageSelect.isHidden = false
imageVisualEffect.isHidden = false
} else {
imageSelect.image = NCImageCache.shared.getImageCheckedNo()
- imageVisualEffect.isHidden = true
}
}
diff --git a/iOSClient/Trash/Cell/NCTrashListCell.swift b/iOSClient/Trash/Cell/NCTrashListCell.swift
index d2df55c700..b9cfaf115a 100644
--- a/iOSClient/Trash/Cell/NCTrashListCell.swift
+++ b/iOSClient/Trash/Cell/NCTrashListCell.swift
@@ -74,7 +74,8 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol {
]
- imageRestore.image = utility.loadImage(named: "restore", color: NCBrandColor.shared.iconColor)
+ imageRestore.image = utility.loadImage(named: "restore", colors: [NCBrandColor.shared.iconColor])
+
imageMore.image = UIImage(systemName: "trash")
imageMore.tintColor = NCBrandColor.shared.iconColor
@@ -112,8 +113,11 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol {
}
if status {
var blurEffectView: UIView?
- blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
- blurEffectView?.backgroundColor = .lightGray
+ var blurEffect: UIVisualEffect?
+ let traitCollectionUserInterfaceStyleDark = traitCollection.userInterfaceStyle == .dark
+ blurEffect = UIBlurEffect(style: traitCollectionUserInterfaceStyleDark ? .dark : .extraLight)
+ blurEffectView = UIVisualEffectView(effect: blurEffect)
+ blurEffectView?.backgroundColor = traitCollectionUserInterfaceStyleDark ? .black : .lightGray
blurEffectView?.frame = self.bounds
blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageSelect.image = NCImageCache.shared.getImageCheckedYes()
diff --git a/iOSClient/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift
index 94a2df94be..849dae9472 100644
--- a/iOSClient/Trash/NCTrash+CollectionView.swift
+++ b/iOSClient/Trash/NCTrash+CollectionView.swift
@@ -29,14 +29,16 @@ import Foundation
extension NCTrash: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- guard let resultTableTrash = datasource?[indexPath.item] else { return }
+ let resultTableTrash = datasource[indexPath.item]
+
guard !isEditMode else {
- if let index = selectOcId.firstIndex(of: resultTableTrash.fileId) {
- selectOcId.remove(at: index)
+ if let index = fileSelect.firstIndex(of: resultTableTrash.fileId) {
+ fileSelect.remove(at: index)
} else {
- selectOcId.append(resultTableTrash.fileId)
+ fileSelect.append(resultTableTrash.fileId)
}
collectionView.reloadItems(at: [indexPath])
+ tabBarSelect.update(selectOcId: fileSelect)
setNavigationRightItems()
return
}
@@ -55,8 +57,10 @@ extension NCTrash: UICollectionViewDelegate {
extension NCTrash: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ let numberOfItems = datasource.count
+ emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
setNavigationRightItems()
- return datasource?.count ?? 0
+ return numberOfItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
@@ -73,14 +77,14 @@ extension NCTrash: UICollectionViewDataSource {
gridCell.delegate = self
cell = gridCell
}
- guard let resultTableTrash = datasource?[indexPath.item] else { return cell }
-
+
+ let resultTableTrash = datasource[indexPath.item]
cell.imageItem.contentMode = .scaleAspectFit
if resultTableTrash.iconName.isEmpty {
image = NCImageCache.shared.getImageFile()
} else {
- image = NCUtility().loadImage(named: resultTableTrash.iconName, useTypeIconFile: true, account: resultTableTrash.account)
+ image = UIImage(named: resultTableTrash.iconName)
}
if let imageIcon = utility.getImage(ocId: resultTableTrash.fileId,
@@ -157,7 +161,7 @@ extension NCTrash: UICollectionViewDataSource {
}
header.delegate = self
- header.setStatusButtonsView(enable: !(datasource?.isEmpty ?? false))
+ header.setStatusButtonsView(enable: !datasource.isEmpty)
header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
header.setButtonsView(height: NCGlobal.shared.heightButtonsView)
header.setRichWorkspaceHeight(0)
@@ -169,7 +173,6 @@ extension NCTrash: UICollectionViewDataSource {
} else {
guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter
else { return UICollectionReusableView() }
- guard let datasource else { return footer }
footer.setTitleLabel(setTextFooter(datasource: datasource))
footer.separatorIsHidden(true)
return footer
@@ -181,8 +184,13 @@ extension NCTrash: UICollectionViewDataSource {
extension NCTrash: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+ if datasource.isEmpty {
+ let height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: -20)
+ return CGSize(width: collectionView.frame.width, height: height)
+ }
return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightButtonsView)
}
+
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 85)
}
diff --git a/iOSClient/Trash/NCTrash.swift b/iOSClient/Trash/NCTrash.swift
index 9d588303a9..074de2955b 100644
--- a/iOSClient/Trash/NCTrash.swift
+++ b/iOSClient/Trash/NCTrash.swift
@@ -27,7 +27,7 @@ import UIKit
import NextcloudKit
import RealmSwift
-class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegate, NCSectionHeaderMenuDelegate {
+class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegate, NCSectionHeaderMenuDelegate, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
var filePath = ""
@@ -37,13 +37,14 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
let database = NCManageDatabase.shared
let utility = NCUtility()
var isEditMode = false
- var selectOcId: [String] = []
+ var fileSelect: [String] = []
var tabBarSelect: NCTrashSelectTabBar!
var datasource: [tableTrash]?
var layoutForView: NCDBLayoutForView?
var listLayout: NCListLayout!
var gridLayout: NCGridLayout!
var layoutKey = NCGlobal.shared.layoutViewTrash
+ var layoutType = NCGlobal.shared.layoutList
let refreshControl = UIRefreshControl()
var filename: String?
@@ -51,6 +52,11 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
var session: NCSession.Session {
NCSession.shared.getSession(controller: tabBarController)
}
+
+ var serverUrl = ""
+ var selectableDataSource: [RealmSwiftObject] { datasource }
+ private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ var emptyDataSet: NCEmptyDataSet?
var controller: NCMainTabBarController? {
self.tabBarController as? NCMainTabBarController
@@ -60,14 +66,14 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
override func viewDidLoad() {
super.viewDidLoad()
- navigationController?.setNavigationBarAppearance()
+ tabBarSelect = NCTrashSelectTabBar(tabBarController: tabBarController, delegate: self)
+ serverUrl = utilityFileSystem.getHomeServer(session: session)
view.backgroundColor = .systemBackground
collectionView.register(UINib(nibName: "NCTrashListCell", bundle: nil), forCellWithReuseIdentifier: "listCell")
collectionView.register(UINib(nibName: "NCTrashGridCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
@@ -79,25 +85,29 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// Add Refresh Control
collectionView.refreshControl = refreshControl
- refreshControl.tintColor = NCBrandColor.shared.textColor2
+ refreshControl.tintColor = .gray //NCBrandColor.shared.textColor2
refreshControl.action(for: .valueChanged) { _ in
Task {
await self.loadListingTrash()
}
}
+ // Empty
+ emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView, delegate: self)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(changeLayout(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeLayout), object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
- if tabBarSelect == nil {
- tabBarSelect = NCTrashSelectTabBar(controller: tabBarController, viewController: self, delegate: self)
- }
+ appDelegate.activeViewController = self
navigationController?.setNavigationBarAppearance()
navigationItem.title = titleCurrentFolder
layoutForView = self.database.getLayoutForView(account: session.account, key: NCGlobal.shared.layoutViewTrash, serverUrl: "")
+ gridLayout.column = CGFloat(layoutForView?.columnGrid ?? 3)
if layoutForView?.layout == NCGlobal.shared.layoutList {
collectionView.collectionViewLayout = listLayout
@@ -112,6 +122,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
await self.reloadDataSource()
await loadListingTrash()
}
+ AnalyticsHelper.shared.trackEvent(eventName: .SCREEN_EVENT__DELETED_FILES)
}
override func viewWillDisappear(_ animated: Bool) {
@@ -123,6 +134,53 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// Cancel Queue & Retrieves Properties
NCNetworking.shared.downloadThumbnailTrashQueue.cancelAll()
+ dataSourceTask?.cancel()
+ isEditMode = false
+ }
+
+ override func viewWillLayoutSubviews() {
+ super.viewWillLayoutSubviews()
+
+ if let frame = tabBarController?.tabBar.frame {
+ tabBarSelect.hostingController?.view.frame = frame
+ }
+ }
+
+ // MARK: - Layout
+
+ @objc func changeLayout(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let layoutForView = userInfo["layoutForView"] as? NCDBLayoutForView,
+ account == session.account,
+ serverUrl == self.serverUrl
+ else { return }
+
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ layoutForView.layout = layoutForView.layout
+ self.layoutType = layoutForView.layout
+// self.reloadDataSource()
+ collectionView.reloadData()
+
+ switch layoutForView.layout {
+ case NCGlobal.shared.layoutList:
+ self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
+ case NCGlobal.shared.layoutGrid:
+ self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
+ default:
+ break
+ }
+
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ }
+
+ // MARK: - Empty
+
+ func emptyDataSetView(_ view: NCEmptyView) {
+ view.emptyImage.image = UIImage(named: "trash")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ view.emptyTitle.text = NSLocalizedString("_trash_no_trash_", comment: "")
+ view.emptyDescription.text = NSLocalizedString("_trash_no_trash_description_", comment: "")
}
// MARK: TAP EVENT
@@ -170,7 +228,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
func tapButtonOrder(_ sender: Any) {
let sortMenu = NCSortMenu()
- sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
+ sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
}
func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { }
@@ -179,7 +237,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// MARK: - DataSource
- func reloadDataSource(withQueryDB: Bool = true) async {
+ @objc func reloadDataSource(withQueryDB: Bool = true) async {
let results = await self.database.getTableTrashAsync(filePath: getFilePath(), account: session.account)
await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
From bddb0276357d048000c73c7763de31f4a24c52d3 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 19:13:16 +0530
Subject: [PATCH 034/177] NMC 2341 - NCTrashView updated changes
---
.../Trash/Cell/NCTrashCellProtocol.swift | 5 +-
iOSClient/Trash/NCTrash+CollectionView.swift | 34 +++----
.../Trash/NCTrash+SelectTabBarDelegate.swift | 80 +++++++++++-----
iOSClient/Trash/NCTrash.swift | 95 +++++++++----------
iOSClient/Trash/NCTrashSelectTabBar.swift | 54 ++++-------
5 files changed, 140 insertions(+), 128 deletions(-)
diff --git a/iOSClient/Trash/Cell/NCTrashCellProtocol.swift b/iOSClient/Trash/Cell/NCTrashCellProtocol.swift
index 8c82abf6db..77f8e55ead 100644
--- a/iOSClient/Trash/Cell/NCTrashCellProtocol.swift
+++ b/iOSClient/Trash/Cell/NCTrashCellProtocol.swift
@@ -48,11 +48,12 @@ extension NCTrashCellProtocol where Self: UICollectionViewCell {
self.labelInfo?.text = dateFormatter.string(from: tableTrash.trashbinDeletionTime as Date)
}
if tableTrash.directory {
- self.imageItem.image = NCImageCache.shared.getFolder(account: tableTrash.account)
+ self.imageItem.image = NCImageCache.shared.getFolder()
} else {
self.imageItem.image = image
- self.labelInfo?.text = (self.labelInfo?.text ?? "") + " · " + NCUtilityFileSystem().transformedSize(tableTrash.size)
+// self.labelInfo?.text = (self.labelInfo?.text ?? "") + " · " + NCUtilityFileSystem().transformedSize(tableTrash.size)
}
+ self.labelInfo?.text = (self.labelInfo?.text ?? "") + " · " + NCUtilityFileSystem().transformedSize(tableTrash.size)
self.accessibilityLabel = tableTrash.trashbinFileName + ", " + (self.labelInfo?.text ?? "")
}
}
diff --git a/iOSClient/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift
index 849dae9472..c14ce8b394 100644
--- a/iOSClient/Trash/NCTrash+CollectionView.swift
+++ b/iOSClient/Trash/NCTrash+CollectionView.swift
@@ -27,9 +27,10 @@ import Foundation
// MARK: UICollectionViewDelegate
extension NCTrash: UICollectionViewDelegate {
-
+
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- let resultTableTrash = datasource[indexPath.item]
+
+ guard let resultTableTrash = datasource?[indexPath.item] else { return }
guard !isEditMode else {
if let index = fileSelect.firstIndex(of: resultTableTrash.fileId) {
@@ -57,10 +58,9 @@ extension NCTrash: UICollectionViewDelegate {
extension NCTrash: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
- let numberOfItems = datasource.count
- emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
+ emptyDataSet?.numberOfItemsInSection(datasource?.count ?? 0, section: section)
setNavigationRightItems()
- return numberOfItems
+ return datasource?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
@@ -78,7 +78,8 @@ extension NCTrash: UICollectionViewDataSource {
cell = gridCell
}
- let resultTableTrash = datasource[indexPath.item]
+ guard let resultTableTrash = datasource?[indexPath.item] else { return cell }
+
cell.imageItem.contentMode = .scaleAspectFit
if resultTableTrash.iconName.isEmpty {
@@ -87,24 +88,21 @@ extension NCTrash: UICollectionViewDataSource {
image = UIImage(named: resultTableTrash.iconName)
}
- if let imageIcon = utility.getImage(ocId: resultTableTrash.fileId,
- etag: resultTableTrash.fileName,
- ext: NCGlobal.shared.previewExt512,
- userId: session.userId,
- urlBase: session.urlBase) {
+ if let imageIcon = utility.getImage(ocId: resultTableTrash.fileId, etag: resultTableTrash.fileName, ext: NCGlobal.shared.previewExt512) {
image = imageIcon
cell.imageItem.contentMode = .scaleAspectFill
} else {
if resultTableTrash.hasPreview {
if NCNetworking.shared.downloadThumbnailTrashQueue.operations.filter({ ($0 as? NCOperationDownloadThumbnailTrash)?.fileId == resultTableTrash.fileId }).isEmpty {
- NCNetworking.shared.downloadThumbnailTrashQueue.addOperation(NCOperationDownloadThumbnailTrash(fileId: resultTableTrash.fileId, fileName: resultTableTrash.fileName, session: session, collectionView: collectionView))
+ NCNetworking.shared.downloadThumbnailTrashQueue.addOperation(NCOperationDownloadThumbnailTrash(fileId: resultTableTrash.fileId, fileName: resultTableTrash.fileName, account: session.account, collectionView: collectionView))
}
}
}
+
cell.account = resultTableTrash.account
cell.objectId = resultTableTrash.fileId
cell.setupCellUI(tableTrash: resultTableTrash, image: image)
- cell.selected(selectOcId.contains(resultTableTrash.fileId), isEditMode: isEditMode, account: resultTableTrash.account)
+ cell.selected(fileSelect.contains(resultTableTrash.fileId), isEditMode: isEditMode, account: resultTableTrash.account)
return cell
}
@@ -161,7 +159,7 @@ extension NCTrash: UICollectionViewDataSource {
}
header.delegate = self
- header.setStatusButtonsView(enable: !datasource.isEmpty)
+ header.setStatusButtonsView(enable: !(datasource?.isEmpty ?? false))
header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
header.setButtonsView(height: NCGlobal.shared.heightButtonsView)
header.setRichWorkspaceHeight(0)
@@ -173,8 +171,10 @@ extension NCTrash: UICollectionViewDataSource {
} else {
guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter
else { return UICollectionReusableView() }
- footer.setTitleLabel(setTextFooter(datasource: datasource))
- footer.separatorIsHidden(true)
+ if let datasource {
+ footer.setTitleLabel(setTextFooter(datasource: datasource))
+ footer.separatorIsHidden(true)
+ }
return footer
}
}
@@ -184,7 +184,7 @@ extension NCTrash: UICollectionViewDataSource {
extension NCTrash: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
- if datasource.isEmpty {
+ if let datasource, datasource.isEmpty {
let height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: 0, landscapeOffset: -20)
return CGSize(width: collectionView.frame.width, height: height)
}
diff --git a/iOSClient/Trash/NCTrash+SelectTabBarDelegate.swift b/iOSClient/Trash/NCTrash+SelectTabBarDelegate.swift
index 677047688c..14b0e071ca 100644
--- a/iOSClient/Trash/NCTrash+SelectTabBarDelegate.swift
+++ b/iOSClient/Trash/NCTrash+SelectTabBarDelegate.swift
@@ -22,7 +22,7 @@
import Foundation
import UIKit
-extension NCTrash: NCTrashSelectTabBarDelegate {
+extension NCTrash: NCTrashSelectTabBarDelegate, NCSelectableNavigationView {
func onListSelected() {
if layoutForView?.layout == NCGlobal.shared.layoutGrid {
layoutForView?.layout = NCGlobal.shared.layoutList
@@ -44,51 +44,81 @@ extension NCTrash: NCTrashSelectTabBarDelegate {
}
func selectAll() {
- guard let datasource else { return }
- if !selectOcId.isEmpty, datasource.count == selectOcId.count {
- selectOcId = []
+ if !fileSelect.isEmpty, datasource?.count == fileSelect.count {
+ fileSelect = []
} else {
- selectOcId = datasource.compactMap({ $0.fileId })
+ fileSelect = (datasource?.compactMap({ $0.fileId }))!
}
- tabBarSelect.update(selectOcId: selectOcId)
+ tabBarSelect.update(selectOcId: fileSelect)
collectionView.reloadData()
}
func recover() {
- let ids = selectOcId.map { $0 }
+ fileSelect.forEach(restoreItem)
setEditMode(false)
-
- Task {
- for id in ids {
- await restoreItem(with: id)
- }
- }
}
func delete() {
- let ids = selectOcId.map { $0 }
+ let ocIds = fileSelect.map { $0 }
setEditMode(false)
Task {
- if ids.count > 0, ids.count == datasource?.count {
+ if ocIds.count > 0, ocIds.count == datasource?.count {
await emptyTrash()
} else {
- await self.deleteItems(with: ids)
+ await self.deleteItems(with: ocIds)
}
}
}
func setEditMode(_ editMode: Bool) {
- Task {
- isEditMode = editMode
- selectOcId.removeAll()
+ isEditMode = editMode
+ fileSelect.removeAll()
- navigationItem.hidesBackButton = editMode
- navigationController?.interactivePopGestureRecognizer?.isEnabled = !editMode
-
- await (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
-
- collectionView.reloadData()
+ navigationController?.interactivePopGestureRecognizer?.isEnabled = !editMode
+ navigationItem.hidesBackButton = editMode
+ DispatchQueue.main.async {
+ self.collectionView.reloadData()
+ self.setNavigationRightItems()
}
}
+
+ func setNavigationRightItems(enableMenu: Bool = false) {
+ if isEditMode {
+ let more = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain) { self.presentMenu(with: self.selectActions)}
+ navigationItem.rightBarButtonItems = [more]
+ } else {
+ let select = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain) { self.toggleSelect() }
+ let notification = UIBarButtonItem(image: UIImage(systemName: "bell"), style: .plain, action: tapNotification)
+ if layoutKey == NCGlobal.shared.layoutViewFiles {
+ navigationItem.rightBarButtonItems = [select, notification]
+ } else {
+ navigationItem.rightBarButtonItems = [select]
+ }
+ }
+ guard layoutKey == NCGlobal.shared.layoutViewFiles else { return }
+ navigationItem.title = titleCurrentFolder
+ }
+
+ func createMenuActions() -> [NCMenuAction] {
+// guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: "") else { return [] }
+//
+// let select = UIAction(title: NSLocalizedString("_select_", comment: ""), image: .init(systemName: "checkmark.circle"), attributes: datasource.isEmpty ? .disabled : []) { _ in
+// self.setEditMode(true)
+// }
+//
+// let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: .init(systemName: "list.bullet"), state: layoutForView.layout == NCGlobal.shared.layoutList ? .on : .off) { _ in
+// self.onListSelected()
+//// self.setNavigationRightItems()
+// }
+//
+// let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: .init(systemName: "square.grid.2x2"), state: layoutForView.layout == NCGlobal.shared.layoutGrid ? .on : .off) { _ in
+// self.onGridSelected()
+//// self.setNavigationRightItems()
+// }
+//
+// let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid])
+//
+ return []//[select, viewStyleSubmenu]
+ }
}
diff --git a/iOSClient/Trash/NCTrash.swift b/iOSClient/Trash/NCTrash.swift
index 074de2955b..8a0d7c0ab2 100644
--- a/iOSClient/Trash/NCTrash.swift
+++ b/iOSClient/Trash/NCTrash.swift
@@ -25,6 +25,7 @@
import UIKit
import NextcloudKit
+import Realm
import RealmSwift
class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegate, NCSectionHeaderMenuDelegate, NCEmptyDataSetDelegate {
@@ -33,6 +34,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
var filePath = ""
var titleCurrentFolder = NSLocalizedString("_trash_view_", comment: "")
var blinkFileId: String?
+ var dataSourceTask: URLSessionTask?
let utilityFileSystem = NCUtilityFileSystem()
let database = NCManageDatabase.shared
let utility = NCUtility()
@@ -47,33 +49,34 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
var layoutType = NCGlobal.shared.layoutList
let refreshControl = UIRefreshControl()
var filename: String?
-
- @MainActor
var session: NCSession.Session {
NCSession.shared.getSession(controller: tabBarController)
}
- var serverUrl = ""
- var selectableDataSource: [RealmSwiftObject] { datasource }
- private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
- var emptyDataSet: NCEmptyDataSet?
-
var controller: NCMainTabBarController? {
self.tabBarController as? NCMainTabBarController
}
+ var serverUrl = ""
+ var selectableDataSource: [RealmSwiftObject] { datasource ?? [] }
+ private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ var emptyDataSet: NCEmptyDataSet?
+
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
+
tabBarSelect = NCTrashSelectTabBar(tabBarController: tabBarController, delegate: self)
serverUrl = utilityFileSystem.getHomeServer(session: session)
view.backgroundColor = .systemBackground
+ self.navigationController?.navigationBar.prefersLargeTitles = true
collectionView.register(UINib(nibName: "NCTrashListCell", bundle: nil), forCellWithReuseIdentifier: "listCell")
collectionView.register(UINib(nibName: "NCTrashGridCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
+// collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
@@ -85,12 +88,9 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// Add Refresh Control
collectionView.refreshControl = refreshControl
- refreshControl.tintColor = .gray //NCBrandColor.shared.textColor2
- refreshControl.action(for: .valueChanged) { _ in
- Task {
- await self.loadListingTrash()
- }
- }
+ refreshControl.tintColor = .gray
+ refreshControl.addTarget(self, action: #selector(loadListingTrash), for: .valueChanged)
+
// Empty
emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView, delegate: self)
@@ -116,22 +116,18 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
}
isEditMode = false
+ setNavigationRightItems()
- Task {
- await (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
- await self.reloadDataSource()
- await loadListingTrash()
- }
+ reloadDataSource()
+ loadListingTrash(nil)
+
AnalyticsHelper.shared.trackEvent(eventName: .SCREEN_EVENT__DELETED_FILES)
+
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
- Task {
- await NCNetworking.shared.networkingTasks.cancel(identifier: "NCTrash")
- }
-
// Cancel Queue & Retrieves Properties
NCNetworking.shared.downloadThumbnailTrashQueue.cancelAll()
dataSourceTask?.cancel()
@@ -185,11 +181,9 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// MARK: TAP EVENT
- func tapRestoreListItem(with id: String, image: UIImage?, sender: Any) {
+ func tapRestoreListItem(with ocId: String, image: UIImage?, sender: Any) {
if !isEditMode {
- Task {
- await restoreItem(with: id)
- }
+ restoreItem(with: ocId)
} else if let button = sender as? UIView {
let buttonPosition = button.convert(CGPoint.zero, to: collectionView)
let indexPath = collectionView.indexPathForItem(at: buttonPosition)
@@ -228,6 +222,7 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
func tapButtonOrder(_ sender: Any) {
let sortMenu = NCSortMenu()
+// sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
}
@@ -237,28 +232,32 @@ class NCTrash: UIViewController, NCTrashListCellDelegate, NCTrashGridCellDelegat
// MARK: - DataSource
- @objc func reloadDataSource(withQueryDB: Bool = true) async {
- let results = await self.database.getTableTrashAsync(filePath: getFilePath(), account: session.account)
-
- await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
-
- await MainActor.run {
- self.datasource = results
- self.collectionView.reloadData()
-
- guard let blinkFileId = self.blinkFileId else { return }
-
- for itemIx in 0..
Date: Mon, 9 Oct 2023 12:48:10 +0530
Subject: [PATCH 035/177] NMC 2346 - More Details option removed from file
conflict dialogue alert
---
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift | 5 -----
1 file changed, 5 deletions(-)
diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
index 570576ed70..38197c7a31 100644
--- a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
+++ b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
@@ -169,11 +169,6 @@ class NCCreateFormUploadConflict: UIViewController {
self.buttonContinueTouch(action)
}))
- // MORE
- conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_more_action_title_", comment: ""), style: .default, handler: { _ in
- self.blurView.removeFromSuperview()
- }))
-
// CANCEL
conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_keep_existing_action_title_", comment: ""), style: .cancel, handler: { _ in
self.dismiss(animated: true) {
From 74d340c3fab19ca8a9ce58cc6babc3a2a4ed317c Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Fri, 13 Oct 2023 15:15:47 +0530
Subject: [PATCH 036/177] NMC 2575 - App update using remote config
---
iOSClient/AppUpdate/AppUpdater.swift | 91 ++++++++++++++++++++++++++++
iOSClient/SceneDelegate.swift | 39 +++++++++++-
2 files changed, 129 insertions(+), 1 deletion(-)
create mode 100644 iOSClient/AppUpdate/AppUpdater.swift
diff --git a/iOSClient/AppUpdate/AppUpdater.swift b/iOSClient/AppUpdate/AppUpdater.swift
new file mode 100644
index 0000000000..9fab18b4ff
--- /dev/null
+++ b/iOSClient/AppUpdate/AppUpdater.swift
@@ -0,0 +1,91 @@
+//
+// AppUpdater.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 09/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import FirebaseRemoteConfig
+
+struct AppUpdaterKey {
+ static let lastUpdateCheckDate : String = "lastUpdateCheckDate"
+}
+
+class AppUpdater {
+ func checkForUpdate() {
+ checkUpdate{ (version, isForceupdate) in
+ DispatchQueue.main.async {
+ if let version = version, let isForceupdate = isForceupdate {
+ if (isForceupdate) {
+ self.showUpdateAlert(version: version, isForceUpdate: isForceupdate)
+ } else {
+ if self.checkLastUpdate() {
+ self.showUpdateAlert(version: version, isForceUpdate: isForceupdate)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ func showUpdateAlert(version: String, isForceUpdate: Bool){
+ guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let viewControlller = appDelegate.window?.rootViewController else { return }
+ let descriptionMsg = String(format: NSLocalizedString("update_description", comment: ""), version)
+ let alert = UIAlertController(title: NSLocalizedString("update_available", comment: ""), message: descriptionMsg, preferredStyle: .alert)
+ let updateAction = UIAlertAction(title: NSLocalizedString("update", comment: ""), style: .default, handler: { action in
+ guard let url = URL(string: NCBrandOptions.shared.appStoreUrl) else { return }
+ UIApplication.shared.open(url)
+ })
+ alert.addAction(updateAction)
+ if !isForceUpdate {
+ alert.addAction(UIAlertAction(title: NSLocalizedString("not_now", comment: ""), style: .default, handler: { action in
+ self.saveAppUpdateCheckDate()
+ }))
+ }
+ alert.preferredAction = updateAction
+ viewControlller.present(alert, animated: true, completion: {})
+ }
+
+ func checkLastUpdate() -> Bool {
+ if let lastUpdateCheckDate = UserDefaults.standard.object(forKey: AppUpdaterKey.lastUpdateCheckDate) as? Date {
+ return daysBetweenDate(from: lastUpdateCheckDate) > 7
+ } else {
+ return true
+ }
+ }
+
+ func checkUpdate(completion: @escaping (String?, Bool?) -> Void) {
+ let remoteConfig = RemoteConfig.remoteConfig()
+ remoteConfig.fetch(withExpirationDuration: 1) { (status, error) in
+ if status == .success {
+ remoteConfig.activate { value, error in
+ // Remote config values fetched successfully
+ let iOSVersion = remoteConfig["ios_app_version"].stringValue ?? "default_value"
+ let isForcheUpdate = remoteConfig["ios_force_update"].boolValue
+ if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+ if iOSVersion != currentVersion {
+ // There is an update available
+ completion(iOSVersion,isForcheUpdate)
+ } else {
+ completion(nil, nil)
+ }
+ }
+ }
+ } else {
+ // Handle error
+ print("Error fetching remote config: \(error?.localizedDescription ?? "Unknown error")")
+ completion(nil, nil)
+ }
+ }
+ }
+
+ func saveAppUpdateCheckDate() {
+ UserDefaults.standard.setValue(Date(), forKey: AppUpdaterKey.lastUpdateCheckDate)
+ }
+
+ func daysBetweenDate(from date: Date) -> Int {
+ return Calendar.current.dateComponents([.day], from: date, to: Date()).day ?? 0
+ }
+}
diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift
index 5aa8acfc6a..6d8685bade 100644
--- a/iOSClient/SceneDelegate.swift
+++ b/iOSClient/SceneDelegate.swift
@@ -112,7 +112,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
NCPreferences().removeAll()
if let bundleID = Bundle.main.bundleIdentifier {
+ let lastUpdateCheckDate = UserDefaults.standard.object(forKey: AppUpdaterKey.lastUpdateCheckDate)
UserDefaults.standard.removePersistentDomain(forName: bundleID)
+ if lastUpdateCheckDate != nil {
+ UserDefaults.standard.setValue(lastUpdateCheckDate, forKey: AppUpdaterKey.lastUpdateCheckDate)
+ }
}
if NCBrandOptions.shared.disable_intro {
@@ -208,13 +212,46 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let session = SceneManager.shared.getSession(scene: scene)
let controller = SceneManager.shared.getController(scene: scene)
- activateSceneForAccount(scene, account: session.account, controller: controller)
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+ Task {
+ if let tableAccount = await self.database.getTableAccountAsync(account: session.account) {
+ let num = await NCAutoUpload.shared.initAutoUpload(tblAccount: tableAccount)
+ nkLog(start: "Auto upload with \(num) photo")
+ }
+ }
+ }
+ AppUpdater().checkForUpdate()
+ NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
}
func sceneDidBecomeActive(_ scene: UIScene) {
+ let session = SceneManager.shared.getSession(scene: scene)
+ let controller = SceneManager.shared.getController(scene: scene)
+ NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene did become active")
+
hidePrivacyProtectionWindow()
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
+ NCService().startRequestServicesServer(account: session.account, controller: controller)
+ }
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
+ Task {
+ await NCNetworking.shared.verifyZombie()
+ }
+ }
+
+ NotificationCenter.default.postOnMainThread(name: global.notificationCenterRichdocumentGrabFocus)
+
}
+// func sceneDidBecomeActive(_ scene: UIScene) {
+// let session = SceneManager.shared.getSession(scene: scene)
+// guard !session.account.isEmpty else { return }
+//
+// hidePrivacyProtectionWindow()
+// }
+
func sceneWillResignActive(_ scene: UIScene) {
nkLog(debug: "Scene will resign active")
From 5caacc9b380f912f64266ae81a93e63b542b5c25 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 8 Apr 2025 14:08:35 +0530
Subject: [PATCH 037/177] NMC 2575 - App update changes
---
Nextcloud.xcodeproj/project.pbxproj | 15 +++++++++++++++
iOSClient/SceneDelegate.swift | 15 +++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index d3dc9dee77..55021d5e3b 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,9 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B5D45E732DA5172900773929 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D45E712DA5172900773929 /* AppUpdater.swift */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1368,6 +1371,8 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B5D45E712DA5172900773929 /* AppUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2211,6 +2216,14 @@
path = Advanced;
sourceTree = "";
};
+ B5D45E722DA5172900773929 /* AppUpdate */ = {
+ isa = PBXGroup;
+ children = (
+ B5D45E712DA5172900773929 /* AppUpdater.swift */,
+ );
+ path = AppUpdate;
+ sourceTree = "";
+ };
C0046CDB2A17B98400D87C9D /* NextcloudUITests */ = {
isa = PBXGroup;
children = (
@@ -3357,6 +3370,7 @@
isa = PBXGroup;
children = (
AA517BB42D66149900F8D37C /* .tx */,
+ B5D45E722DA5172900773929 /* AppUpdate */,
F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */,
F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */,
F7CF067A2E0FF38F0063AD04 /* NCAppStateManager.swift */,
@@ -4772,6 +4786,7 @@
F7D4BF402CA2E8D800A5E746 /* TOPasscodeButtonLabel.m in Sources */,
F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */,
F7D4BF422CA2E8D800A5E746 /* TOPasscodeSettingsViewController.m in Sources */,
+ B5D45E732DA5172900773929 /* AppUpdater.swift in Sources */,
F7D4BF432CA2E8D800A5E746 /* TOPasscodeCircleImage.m in Sources */,
F7D4BF442CA2E8D800A5E746 /* TOPasscodeView.m in Sources */,
F7D4BF452CA2E8D800A5E746 /* TOPasscodeCircleButton.m in Sources */,
diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift
index 6d8685bade..46b6ae8bfe 100644
--- a/iOSClient/SceneDelegate.swift
+++ b/iOSClient/SceneDelegate.swift
@@ -221,6 +221,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}
}
AppUpdater().checkForUpdate()
+
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
}
@@ -229,6 +230,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let controller = SceneManager.shared.getController(scene: scene)
NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Scene did become active")
+ let oldVersion = UserDefaults.standard.value(forKey: NCSettingsBundleHelper.SettingsBundleKeys.BuildVersionKey) as? String
+ AppUpdater().checkForUpdate()
+ AnalyticsHelper.shared.trackAppVersion(oldVersion: oldVersion)
+ if let userAccount = NCManageDatabase.shared.getActiveTableAccount() {
+ AnalyticsHelper.shared.trackUsedStorageData(quotaUsed: userAccount.quotaUsed)
+ }
+
+ NCSettingsBundleHelper.setVersionAndBuildNumber()
+ NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5)
+
+// if !NCAskAuthorization().isRequesting {
+// NCPasscode.shared.hidePrivacyProtectionWindow()
+// }
+
hidePrivacyProtectionWindow()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
From a3e0ad732219fc29e4d43e0c19a69fd4a993bc13 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 21:09:12 +0530
Subject: [PATCH 038/177] NMC 2575 - App update changes
---
iOSClient/AppUpdate/AppUpdater.swift | 19 ++++++++++++++++---
iOSClient/SceneDelegate.swift | 4 ++++
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/iOSClient/AppUpdate/AppUpdater.swift b/iOSClient/AppUpdate/AppUpdater.swift
index 9fab18b4ff..15dd5f6991 100644
--- a/iOSClient/AppUpdate/AppUpdater.swift
+++ b/iOSClient/AppUpdate/AppUpdater.swift
@@ -62,16 +62,29 @@ class AppUpdater {
if status == .success {
remoteConfig.activate { value, error in
// Remote config values fetched successfully
- let iOSVersion = remoteConfig["ios_app_version"].stringValue ?? "default_value"
+ let iOSVersionString = remoteConfig["ios_app_version"].stringValue ?? "default_value"
let isForcheUpdate = remoteConfig["ios_force_update"].boolValue
- if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+ if let currentVersionString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
+ let currentVersion = Int(currentVersionString.replacingOccurrences(of: ".", with: "")),
+ let iOSVersion = Int(iOSVersionString.replacingOccurrences(of: ".", with: "")) {
+// if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
if iOSVersion != currentVersion {
// There is an update available
- completion(iOSVersion,isForcheUpdate)
+ completion(iOSVersionString,isForcheUpdate)
} else {
completion(nil, nil)
}
}
+// let iOSVersion = remoteConfig["ios_app_version"].stringValue ?? "default_value"
+// let isForcheUpdate = remoteConfig["ios_force_update"].boolValue
+// if let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+// if iOSVersion != currentVersion {
+// // There is an update available
+// completion(iOSVersion,isForcheUpdate)
+// } else {
+// completion(nil, nil)
+// }
+// }
}
} else {
// Handle error
diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift
index 46b6ae8bfe..e9285019f3 100644
--- a/iOSClient/SceneDelegate.swift
+++ b/iOSClient/SceneDelegate.swift
@@ -481,6 +481,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}
private func showPrivacyProtectionWindow() {
+ guard privacyProtectionWindow == nil else {
+ privacyProtectionWindow?.isHidden = false
+ return
+ }
guard let windowScene = self.window?.windowScene else {
return
}
From 929b660d04ff88100341d9a866487ad8e93b70e1 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Tue, 17 Oct 2023 17:16:37 +0530
Subject: [PATCH 039/177] NMC 1615 - Login screen status bar color update and
title removed
---
iOSClient/Login/NCLoginProvider.swift | 97 ++++++++++++++++++++++-----
1 file changed, 82 insertions(+), 15 deletions(-)
diff --git a/iOSClient/Login/NCLoginProvider.swift b/iOSClient/Login/NCLoginProvider.swift
index f4dc78221a..7e5d050f13 100644
--- a/iOSClient/Login/NCLoginProvider.swift
+++ b/iOSClient/Login/NCLoginProvider.swift
@@ -4,7 +4,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import UIKit
-@preconcurrency import WebKit
+import WebKit
import NextcloudKit
import FloatingPanel
@@ -19,7 +19,9 @@ protocol NCLoginProviderDelegate: AnyObject {
/// View which presents the web view to login at a Nextcloud instance.
///
class NCLoginProvider: UIViewController {
- var webView: WKWebView!
+ var webView: WKWebView?
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let utility = NCUtility()
var titleView: String = ""
var initialURLString = ""
var uiColor: UIColor = .white
@@ -68,16 +70,26 @@ class NCLoginProvider: UIViewController {
return
}
- if let host = url.host {
- titleView = host
-
- if let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(), NCPreferences().getPassword(account: activeTableAccount.account).isEmpty {
- titleView = NSLocalizedString("_user_", comment: "") + " " + activeTableAccount.userId + " " + NSLocalizedString("_in_", comment: "") + " " + host
+ if #available(iOS 13, *) {
+ let keyWindow = UIApplication.shared.connectedScenes
+ .filter({$0.activationState == .foregroundActive})
+ .map({$0 as? UIWindowScene})
+ .compactMap({$0})
+ .first?.windows
+ .filter({$0.isKeyWindow}).first
+ let statusBar = UIView(frame: (keyWindow?.windowScene?.statusBarManager?.statusBarFrame)!)
+ statusBar.backgroundColor = NCBrandColor.shared.customer
+ keyWindow?.addSubview(statusBar)
+ } else {
+ if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
+ statusBar.backgroundColor = NCBrandColor.shared.customer
}
}
-
loadWebPage(url: url)
self.title = titleView
+ self.navigationController?.navigationBar.backgroundColor = NCBrandColor.shared.customer
+
+ appDelegate.timerErrorNetworkingDisabled = true
}
override func viewDidDisappear(_ animated: Bool) {
@@ -86,13 +98,14 @@ class NCLoginProvider: UIViewController {
NCActivityIndicator.shared.stop()
- guard pollingTask != nil else {
- return
- }
-
- nkLog(debug: "Cancelling existing polling task because view did disappear...")
- pollingTask?.cancel()
- pollingTask = nil
+// guard pollingTask != nil else {
+// return
+// }
+//
+// nkLog(debug: "Cancelling existing polling task because view did disappear...")
+// pollingTask?.cancel()
+// pollingTask = nil
+ appDelegate.timerErrorNetworkingDisabled = false
}
// MARK: - Navigation
@@ -293,4 +306,58 @@ extension NCLoginProvider: WKNavigationDelegate {
nkLog(debug: "Web view did finish navigation to \(webView.url?.absoluteString ?? "nil")")
NCActivityIndicator.shared.stop()
}
+
+ // MARK: -
+
+// func createAccount(server: String, username: String, password: String) {
+// var urlBase = server
+// if urlBase.last == "/" { urlBase = String(urlBase.dropLast()) }
+// let account: String = "\(username) \(urlBase)"
+// let user = username
+//
+// NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
+// NextcloudKit.shared.getUserProfile(account: account) { _, userProfile, _, error in
+// if error == .success, let userProfile {
+// NextcloudKit.shared.appendSession(account: account,
+// urlBase: urlBase,
+// user: user,
+// userId: user,
+// password: password,
+// userAgent: userAgent,
+// nextcloudVersion: NCCapabilities.shared.getCapabilities(account: account).capabilityServerVersionMajor,
+// httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost,
+// httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload,
+// httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload,
+// groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
+// NCSession.shared.appendSession(account: account, urlBase: urlBase, user: user, userId: userProfile.userId)
+// NCManageDatabase.shared.deleteAccount(account)
+// NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
+// self.appDelegate.changeAccount(account, userProfile: userProfile) { }
+// let window = UIApplication.shared.firstWindow
+// if window?.rootViewController is NCMainTabBarController {
+// self.dismiss(animated: true)
+// } else {
+// if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+// controller.modalPresentationStyle = .fullScreen
+// controller.view.alpha = 0
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+// UIView.animate(withDuration: 0.5) {
+// controller.view.alpha = 1
+// }
+// }
+// }
+// } else {
+// let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+// self.present(alertController, animated: true)
+// }
+// }
+// }
}
From 0a6708842992baf49416fb96135289806ce8a5de Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 11:54:02 +0530
Subject: [PATCH 040/177] NMC 1615 - Login screens customisation changes
---
Nextcloud.xcodeproj/project.pbxproj | 8 +
iOSClient/Login/NCLogin.storyboard | 15 +
iOSClient/Login/NCLogin.swift | 36 +-
iOSClient/Login/NCLoginProvider.swift | 15 +-
iOSClient/Login/NCLoginWeb.swift | 370 ++++++++++++++++++
.../Login/NCViewCertificateDetails.swift | 5 +-
6 files changed, 437 insertions(+), 12 deletions(-)
create mode 100644 iOSClient/Login/NCLoginWeb.swift
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index d3dc9dee77..693cc7d044 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,9 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B54315302DA647A400981E7E /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543152F2DA647A400981E7E /* NCLoginWeb.swift */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1368,6 +1371,8 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B543152F2DA647A400981E7E /* NCLoginWeb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginWeb.swift; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -3021,6 +3026,8 @@
F7BFFA621A24D7300044ED85 /* Login */ = {
isa = PBXGroup;
children = (
+ B543152F2DA647A400981E7E /* NCLoginWeb.swift */,
+ F3CA33802D106FF900672333 /* Poll */,
F702F2F025EE5CDA008F8E80 /* NCLogin.storyboard */,
F702F2F625EE5CEC008F8E80 /* NCLogin.swift */,
F745B252222D88AE00346520 /* NCLoginQRCode.swift */,
@@ -4963,6 +4970,7 @@
F752BA052E58C05200616A26 /* Maintenance.swift in Sources */,
F763D29D2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
F76882252C0DD1E7001CF441 /* NCSettingsAdvancedModel.swift in Sources */,
+ B54315302DA647A400981E7E /* NCLoginWeb.swift in Sources */,
F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
F78B87E72B62527100C65ADC /* NCMediaDataSource.swift in Sources */,
diff --git a/iOSClient/Login/NCLogin.storyboard b/iOSClient/Login/NCLogin.storyboard
index 981eadcc58..fd3114f79b 100644
--- a/iOSClient/Login/NCLogin.storyboard
+++ b/iOSClient/Login/NCLogin.storyboard
@@ -199,6 +199,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift
index 23bbcb4b32..c9e2e69f8e 100644
--- a/iOSClient/Login/NCLogin.swift
+++ b/iOSClient/Login/NCLogin.swift
@@ -93,6 +93,9 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
baseUrlTextField.alpha = 0.5
urlBase = NCBrandOptions.shared.loginBaseUrl
}
+
+ // qrcode
+ qrCode.setImage(UIImage(named: "qrcode")?.image(color: textColor, size: 100), for: .normal)
// certificate
certificate.setImage(UIImage(named: "certificate")?.image(color: textColor, size: 100), for: .normal)
@@ -330,14 +333,31 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
let loginOptions = NKRequestOptions(customUserAgent: userAgent)
NextcloudKit.shared.getLoginFlowV2(serverUrl: url, options: loginOptions) { [self] token, endpoint, login, _, error in
// Login Flow V2
- if error == .success, let token, let endpoint, let login {
- nkLog(debug: "Successfully received login flow information.")
- let safariVC = NCLoginProvider()
- safariVC.initialURLString = login
- safariVC.uiColor = textColor
- safariVC.delegate = self
- safariVC.startPolling(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login)
- navigationController?.pushViewController(safariVC, animated: true)
+// if error == .success, let token, let endpoint, let login {
+// nkLog(debug: "Successfully received login flow information.")
+// let safariVC = NCLoginProvider()
+// safariVC.initialURLString = login
+// safariVC.uiColor = textColor
+// safariVC.delegate = self
+// safariVC.startPolling(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login)
+// navigationController?.pushViewController(safariVC, animated: true)
+ if error == .success && NCBrandOptions.shared.use_loginflowv2 && token != nil && endpoint != nil && login != nil {
+
+ if let loginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb {
+
+ loginWeb.urlBase = url
+ loginWeb.user = user
+ loginWeb.loginFlowV2Available = true
+ loginWeb.loginFlowV2Token = token!
+ loginWeb.loginFlowV2Endpoint = endpoint!
+ loginWeb.loginFlowV2Login = login!
+
+ self.navigationController?.pushViewController(loginWeb, animated: true)
+ }
+ } else if serverInfo.versionMajor < NCGlobal.shared.nextcloudVersion12 { // No login flow available
+ let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_webflow_not_available_", comment: ""), preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ present(alertController, animated: true, completion: { })
}
}
case .failure(let error):
diff --git a/iOSClient/Login/NCLoginProvider.swift b/iOSClient/Login/NCLoginProvider.swift
index 7e5d050f13..f6f20c9eb5 100644
--- a/iOSClient/Login/NCLoginProvider.swift
+++ b/iOSClient/Login/NCLoginProvider.swift
@@ -92,6 +92,19 @@ class NCLoginProvider: UIViewController {
appDelegate.timerErrorNetworkingDisabled = true
}
+// override func viewDidAppear(_ animated: Bool) {
+// super.viewDidAppear(animated)
+// // Stop timer error network
+//// appDelegate.timerErrorNetworkingDisabled = true
+// if let account = NCManageDatabase.shared.getActiveTableAccount(), NCKeychain().getPassword(account: account.account).isEmpty {
+//
+// let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
+// let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+// present(alertController, animated: true)
+// }
+// }
+
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
nkLog(debug: "Login provider view did disappear.")
@@ -105,7 +118,7 @@ class NCLoginProvider: UIViewController {
// nkLog(debug: "Cancelling existing polling task because view did disappear...")
// pollingTask?.cancel()
// pollingTask = nil
- appDelegate.timerErrorNetworkingDisabled = false
+// appDelegate.timerErrorNetworkingDisabled = false
}
// MARK: - Navigation
diff --git a/iOSClient/Login/NCLoginWeb.swift b/iOSClient/Login/NCLoginWeb.swift
new file mode 100644
index 0000000000..daa39f5e6c
--- /dev/null
+++ b/iOSClient/Login/NCLoginWeb.swift
@@ -0,0 +1,370 @@
+//
+// NCLoginWeb.swift
+// Nextcloud
+//
+// Created by Marino Faggiana on 21/08/2019.
+// Copyright © 2019 Marino Faggiana. All rights reserved.
+//
+// Author Marino Faggiana
+//
+// 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
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+import UIKit
+import WebKit
+import NextcloudKit
+import FloatingPanel
+
+class NCLoginWeb: UIViewController {
+
+ var webView: WKWebView?
+ var newWebView: WKWebView?
+
+ let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+ let utility = NCUtility()
+
+ var titleView: String = ""
+
+ var urlBase = ""
+ var user: String?
+
+ var configServerUrl: String?
+ var configUsername: String?
+ var configPassword: String?
+ var configAppPassword: String?
+
+ var loginFlowV2Available = false
+ var loginFlowV2Token = ""
+ var loginFlowV2Endpoint = ""
+ var loginFlowV2Login = ""
+
+ // MARK: - View Life Cycle
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0
+
+ // load AppConfig
+ if (NCBrandOptions.shared.disable_multiaccount == false) || (NCBrandOptions.shared.disable_multiaccount == true && accountCount == 0) {
+ if let configurationManaged = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"), NCBrandOptions.shared.use_AppConfig {
+
+ if let serverUrl = configurationManaged[NCGlobal.shared.configuration_serverUrl] as? String {
+ self.configServerUrl = serverUrl
+ }
+ if let username = configurationManaged[NCGlobal.shared.configuration_username] as? String, !username.isEmpty, username.lowercased() != "username" {
+ self.configUsername = username
+ }
+ if let password = configurationManaged[NCGlobal.shared.configuration_password] as? String, !password.isEmpty, password.lowercased() != "password" {
+ self.configPassword = password
+ }
+ if let apppassword = configurationManaged[NCGlobal.shared.configuration_apppassword] as? String, !apppassword.isEmpty, apppassword.lowercased() != "apppassword" {
+ self.configAppPassword = apppassword
+ }
+ }
+ }
+
+ if (NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig) && accountCount > 0 {
+ navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(self.closeView(sender:)))
+ }
+
+ if accountCount > 0 {
+ navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "users")!.image(color: .label, size: 35), style: .plain, target: self, action: #selector(self.changeUser(sender:)))
+ }
+
+ let config = WKWebViewConfiguration()
+ config.websiteDataStore = WKWebsiteDataStore.nonPersistent()
+
+ webView = WKWebView(frame: CGRect.zero, configuration: config)
+ webView!.navigationDelegate = self
+// webView!.uiDelegate = self
+ view.addSubview(webView!)
+
+ webView!.translatesAutoresizingMaskIntoConstraints = false
+ webView!.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
+ webView!.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
+ webView!.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
+ webView!.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
+
+ // AppConfig
+ if let serverUrl = configServerUrl {
+ if let username = self.configUsername, let password = configAppPassword {
+ self.createAccount(urlBase: serverUrl, user: username, password: password)
+ return
+ } else if let username = self.configUsername, let password = configPassword {
+ getAppPassword(urlBase: serverUrl, user: username, password: password)
+ return
+ } else {
+ urlBase = serverUrl
+ }
+ }
+
+ // ADD end point for Web Flow
+ if urlBase != NCBrandOptions.shared.linkloginPreferredProviders {
+ if loginFlowV2Available {
+ urlBase = loginFlowV2Login
+ } else {
+ urlBase += "/index.php/login/flow"
+ if let user = self.user {
+ urlBase += "?user=\(user)"
+ }
+ }
+ }
+
+ if let url = URL(string: urlBase) {
+ loadWebPage(webView: webView!, url: url)
+ } else {
+ let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_login_url_error_")
+ NCContentPresenter().showError(error: error, priority: .max)
+ }
+
+ if #available(iOS 13, *) {
+ let keyWindow = UIApplication.shared.connectedScenes
+ .filter({$0.activationState == .foregroundActive})
+ .map({$0 as? UIWindowScene})
+ .compactMap({$0})
+ .first?.windows
+ .filter({$0.isKeyWindow}).first
+ let statusBar = UIView(frame: (keyWindow?.windowScene?.statusBarManager?.statusBarFrame)!)
+ statusBar.backgroundColor = NCBrandColor.shared.customer
+ keyWindow?.addSubview(statusBar)
+ } else {
+ if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
+ statusBar.backgroundColor = NCBrandColor.shared.customer
+ }
+ }
+ self.navigationController?.navigationBar.backgroundColor = NCBrandColor.shared.customer
+ self.edgesForExtendedLayout = [] // iphone 16, white gap between navigaiton and status bar.
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ // Stop timer error network
+ appDelegate.timerErrorNetworking?.invalidate()
+
+ if let account = NCManageDatabase.shared.getActiveTableAccount(), NCKeychain().getPassword(account: account.account).isEmpty {
+
+ let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
+ let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ present(alertController, animated: true)
+ }
+ }
+
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+
+ NCActivityIndicator.shared.stop()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ // Start timer error network
+ appDelegate.startTimerErrorNetworking()
+ }
+
+ func loadWebPage(webView: WKWebView, url: URL) {
+
+ let language = NSLocale.preferredLanguages[0] as String
+ var request = URLRequest(url: url)
+
+ if let deviceName = "\(UIDevice.current.name) (\(NCBrandOptions.shared.brand) iOS)".cString(using: .utf8),
+ let deviceUserAgent = String(cString: deviceName, encoding: .ascii) {
+ webView.customUserAgent = deviceUserAgent
+ } else {
+ webView.customUserAgent = userAgent
+ }
+
+ request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
+ request.addValue(language, forHTTPHeaderField: "Accept-Language")
+
+ webView.load(request)
+ }
+
+ func getAppPassword(urlBase: String, user: String, password: String) {
+
+ NextcloudKit.shared.getAppPassword(url: urlBase, user: user, password: password) { token, _, error in
+ if error == .success, let password = token {
+ self.createAccount(urlBase: urlBase, user: user, password: password)
+ } else {
+ NCContentPresenter().showError(error: error)
+ self.dismiss(animated: true, completion: nil)
+ }
+ }
+ }
+
+ @objc func closeView(sender: UIBarButtonItem) {
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func changeUser(sender: UIBarButtonItem) {
+// toggleMenu()
+ }
+}
+
+extension NCLoginWeb: WKNavigationDelegate {
+
+ func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
+
+ guard let url = webView.url else { return }
+
+ let urlString: String = url.absoluteString.lowercased()
+
+ // prevent http redirection
+ if urlBase.lowercased().hasPrefix("https://") && urlString.lowercased().hasPrefix("http://") {
+ let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_prevent_http_redirection_", comment: ""), preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
+ _ = self.navigationController?.popViewController(animated: true)
+ }))
+ self.present(alertController, animated: true)
+ return
+ }
+
+ if urlString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol) == true && urlString.contains("login") == true {
+
+ var server: String = ""
+ var user: String = ""
+ var password: String = ""
+
+ let keyValue = url.path.components(separatedBy: "&")
+ for value in keyValue {
+ if value.contains("server:") { server = value }
+ if value.contains("user:") { user = value }
+ if value.contains("password:") { password = value }
+ }
+
+ if !server.isEmpty, !user.isEmpty, !password.isEmpty {
+
+ let server: String = server.replacingOccurrences(of: "/server:", with: "")
+ let username: String = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ")
+ let password: String = password.replacingOccurrences(of: "password:", with: "")
+
+ self.createAccount(urlBase: server, user: username, password: password)
+
+ }
+ }
+ }
+
+ func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+ DispatchQueue.global().async {
+ if let serverTrust = challenge.protectionSpace.serverTrust {
+ completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
+ } else {
+ completionHandler(URLSession.AuthChallengeDisposition.useCredential, nil)
+ }
+ }
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ decisionHandler(.allow)
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
+ if let httpResponse = navigationResponse.response as? HTTPURLResponse {
+ print("Response status code: \(httpResponse.statusCode)")
+
+ if httpResponse.statusCode == 403 {
+// showAccessDeniedAlert()
+ closeApp()
+ decisionHandler(.cancel) // Stop loading the page
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+
+
+ // Show an alert instead of closing the app
+ private func showAccessDeniedAlert() {
+ DispatchQueue.main.async {
+ let alert = UIAlertController(title: "Access Denied", message: "You are not authorized to view this page.", preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
+ // Optionally, redirect user
+ self.webView?.load(URLRequest(url: URL(string: "https://www.google.com")!))
+ }))
+ self.present(alert, animated: true, completion: nil)
+ }
+ }
+
+ func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
+ NCActivityIndicator.shared.startActivity(style: .medium, blurEffect: false)
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+
+ NCActivityIndicator.shared.stop()
+
+ if loginFlowV2Available {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+ NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in
+ if error == .success && server != nil && loginName != nil && appPassword != nil {
+ self.createAccount(urlBase: server!, user: loginName!, password: appPassword!)
+ }
+ }
+ }
+ }
+ }
+
+
+ private func closeApp() {
+ // Exit the app immediately
+ exit(0)
+ }
+
+
+ // MARK: -
+
+ func createAccount(urlBase: String, user: String, password: String) {
+ let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+ if let host = URL(string: urlBase)?.host {
+ NCNetworking.shared.writeCertificate(host: host)
+ }
+ NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
+ if error == .success {
+ let window = UIApplication.shared.firstWindow
+ if let controller = window?.rootViewController as? NCMainTabBarController {
+ controller.account = account
+ self.dismiss(animated: true)
+ } else {
+ if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+ AnalyticsHelper.shared.trackUserData()
+ controller.account = account
+ controller.modalPresentationStyle = .fullScreen
+ controller.view.alpha = 0
+
+ window?.rootViewController = controller
+ window?.makeKeyAndVisible()
+
+ if let scene = window?.windowScene {
+ SceneManager.shared.register(scene: scene, withRootViewController: controller)
+ }
+
+ UIView.animate(withDuration: 0.5) {
+ controller.view.alpha = 1
+ }
+ }
+ }
+ } else {
+ let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ self.present(alertController, animated: true)
+ }
+ }
+ }
+
+}
diff --git a/iOSClient/Login/NCViewCertificateDetails.swift b/iOSClient/Login/NCViewCertificateDetails.swift
index 5acc082b94..2a60324a27 100644
--- a/iOSClient/Login/NCViewCertificateDetails.swift
+++ b/iOSClient/Login/NCViewCertificateDetails.swift
@@ -43,10 +43,9 @@ class NCViewCertificateDetails: UIViewController {
var delegate: NCViewCertificateDetailsDelegate?
let utilityFileSystem = NCUtilityFileSystem()
- public var privateKeyString: String = ""
- public var host: String = ""
+ @objc public var host: String = ""
public var fileNamePath: String = ""
- public var certificateTitle = NSLocalizedString("_certificate_view_", comment: "")
+ @objc public var certificateTitle = NSLocalizedString("_certificate_view_", comment: "")
// MARK: - View Life Cycle
From 55db979e2b4dd7ca046442608ed9fc89a2b853e7 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 11:55:14 +0530
Subject: [PATCH 041/177] NMC 2615 - Login screens customisation changes
---
iOSClient/Login/NCLogin.swift | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift
index c9e2e69f8e..d87b901e45 100644
--- a/iOSClient/Login/NCLogin.swift
+++ b/iOSClient/Login/NCLogin.swift
@@ -330,8 +330,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
if let host = URL(string: url)?.host {
NCNetworking.shared.writeCertificate(host: host)
}
- let loginOptions = NKRequestOptions(customUserAgent: userAgent)
- NextcloudKit.shared.getLoginFlowV2(serverUrl: url, options: loginOptions) { [self] token, endpoint, login, _, error in
+ NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { [self] token, endpoint, login, _, error in
// Login Flow V2
// if error == .success, let token, let endpoint, let login {
// nkLog(debug: "Successfully received login flow information.")
From 268e5b3ab363025ddc19e5e2e1146316cc69581f Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 21:19:22 +0530
Subject: [PATCH 042/177] NMC 2615 - Login screens customisation changes
---
iOSClient/Login/NCLogin.storyboard | 20 +-
iOSClient/Login/NCLogin.swift | 121 +++++++--
iOSClient/Login/NCLoginProvider.swift | 239 ++++++------------
iOSClient/Login/NCLoginWeb.swift | 81 +++---
.../Login/NCViewCertificateDetails.storyboard | 23 +-
5 files changed, 234 insertions(+), 250 deletions(-)
diff --git a/iOSClient/Login/NCLogin.storyboard b/iOSClient/Login/NCLogin.storyboard
index fd3114f79b..05d3331812 100644
--- a/iOSClient/Login/NCLogin.storyboard
+++ b/iOSClient/Login/NCLogin.storyboard
@@ -19,14 +19,14 @@
-
+
-
+
@@ -36,7 +36,7 @@
-
+
@@ -53,13 +53,13 @@
-
+
-
+
@@ -74,7 +74,7 @@
-
+
@@ -84,7 +84,7 @@
-
+
@@ -96,7 +96,7 @@
-
+
@@ -111,14 +111,14 @@
-
+
-
+
diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift
index d87b901e45..94b26f5e08 100644
--- a/iOSClient/Login/NCLogin.swift
+++ b/iOSClient/Login/NCLogin.swift
@@ -44,6 +44,10 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
private var p12Data: Data?
private var p12Password: String?
+ var pollTimer: DispatchSourceTimer?
+ var ncLoginPollModel = NCLoginPollModel()
+ var loginFlowInProgress = false
+
// MARK: - View Life Cycle
override func viewDidLoad() {
@@ -83,9 +87,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
loginAddressDetail.textColor = textColor
loginAddressDetail.text = String.localizedStringWithFormat(NSLocalizedString("_login_address_detail_", comment: ""), NCBrandOptions.shared.brand)
- // QR code button
- qrCode.tintColor = NCBrandColor.shared.customer.isTooLight() ? .black : .white
-
// brand
if NCBrandOptions.shared.disable_request_login_url {
baseUrlTextField.isEnabled = false
@@ -129,7 +130,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
if !accountTemp.isEmpty {
self.shareAccounts = accountTemp
let image = NCUtility().loadImage(named: "person.badge.plus")
- let navigationItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(openShareAccountsViewController(_:)))
+ let navigationItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(openShareAccountsViewController))
navigationItem.tintColor = textColor
self.navigationItem.rightBarButtonItem = navigationItem
}
@@ -180,7 +181,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
if !NCManageDatabase.shared.getAllTableAccount().isEmpty,
self.navigationController?.viewControllers.count ?? 0 == 1 {
- let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(actionCancel(_:)))
+ let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(self.actionCancel))
navigationItemCancel.tintColor = textColor
navigationItem.leftBarButtonItem = navigationItemCancel
}
@@ -194,11 +195,20 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
let description = String(format: NSLocalizedString("_add_existing_account_", comment: ""), NCBrandOptions.shared.brand)
NCContentPresenter().alertAction(image: image, contentModeImage: .scaleAspectFit, sizeImage: CGSize(width: 45, height: 45), backgroundColor: backgroundColor, textColor: textColor, title: title, description: description, textCancelButton: "_cancel_", textOkButton: "_ok_", attributes: EKAttributes.topFloat) { identifier in
if identifier == "ok" {
- self.openShareAccountsViewController(nil)
+ self.openShareAccountsViewController()
}
}
}
}
+
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+
+ if navigationController?.isBeingDismissed == true {
+ pollTimer?.cancel()
+ pollTimer = nil
+ }
+ }
private func handleLoginWithAppConfig() {
let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0
@@ -268,7 +278,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
// MARK: - Action
- @objc func actionCancel(_ sender: Any?) {
+ @objc func actionCancel() {
dismiss(animated: true) { }
}
@@ -289,7 +299,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
// MARK: - Share accounts View Controller
- @objc func openShareAccountsViewController(_ sender: Any?) {
+ @objc func openShareAccountsViewController() {
if let shareAccounts = self.shareAccounts, let vc = UIStoryboard(name: "NCShareAccounts", bundle: nil).instantiateInitialViewController() as? NCShareAccounts {
vc.accounts = shareAccounts
vc.enableTimerProgress = false
@@ -326,31 +336,31 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
NextcloudKit.shared.getServerStatus(serverUrl: url) { [self] _, serverInfoResult in
switch serverInfoResult {
- case .success:
+ case .success(let serverInfo):
if let host = URL(string: url)?.host {
NCNetworking.shared.writeCertificate(host: host)
}
- NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { [self] token, endpoint, login, _, error in
+ NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { [self] token, endpoint, login, _, error in
// Login Flow V2
-// if error == .success, let token, let endpoint, let login {
-// nkLog(debug: "Successfully received login flow information.")
-// let safariVC = NCLoginProvider()
-// safariVC.initialURLString = login
-// safariVC.uiColor = textColor
-// safariVC.delegate = self
-// safariVC.startPolling(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login)
-// navigationController?.pushViewController(safariVC, animated: true)
if error == .success && NCBrandOptions.shared.use_loginflowv2 && token != nil && endpoint != nil && login != nil {
-
+
if let loginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb {
-
+
loginWeb.urlBase = url
loginWeb.user = user
loginWeb.loginFlowV2Available = true
loginWeb.loginFlowV2Token = token!
loginWeb.loginFlowV2Endpoint = endpoint!
loginWeb.loginFlowV2Login = login!
-
+
+ self.navigationController?.pushViewController(loginWeb, animated: true)
+ }
+ } else if serverInfo.versionMajor >= NCGlobal.shared.nextcloudVersion12 {
+
+ if let loginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb {
+
+ loginWeb.urlBase = url
+ loginWeb.user = user
self.navigationController?.pushViewController(loginWeb, animated: true)
}
} else if serverInfo.versionMajor < NCGlobal.shared.nextcloudVersion12 { // No login flow available
@@ -444,16 +454,12 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
}
}
-// MARK: - NCShareAccountsDelegate
-
extension NCLogin: NCShareAccountsDelegate {
func selected(url: String, user: String) {
isUrlValid(url: url, user: user)
}
}
-// MARK: - UIDocumentPickerDelegate
-
extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate {
func didAskForClientCertificate() {
let alertNoCertFound = UIAlertController(title: NSLocalizedString("_no_client_cert_found_", comment: ""), message: NSLocalizedString("_no_client_cert_found_desc_", comment: ""), preferredStyle: .alert)
@@ -493,9 +499,68 @@ extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate {
self.present(alertWrongPassword, animated: true)
}
}
+
+// func poll(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) {
+// let queue = DispatchQueue.global(qos: .background)
+// pollTimer = DispatchSource.makeTimerSource(queue: queue)
+//
+// guard let timer = pollTimer else { return }
+//
+// timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1))
+// timer.setEventHandler(handler: {
+// DispatchQueue.main.async {
+// let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+// NextcloudKit.shared.getLoginFlowV2Poll(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint) { [self] server, loginName, appPassword, _, error in
+// if error == .success, let urlBase = server, let user = loginName, let appPassword {
+// loginFlowInProgress = true
+// ncLoginPollModel.isLoading = true
+//
+// NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in
+//
+// if error == .success {
+// let window = UIApplication.shared.firstWindow
+// if let controller = window?.rootViewController as? NCMainTabBarController {
+// controller.account = account
+// controller.dismiss(animated: true, completion: nil)
+// } else {
+// if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+// controller.account = account
+// controller.modalPresentationStyle = .fullScreen
+// controller.view.alpha = 0
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+// UIView.animate(withDuration: 0.5) {
+// controller.view.alpha = 1
+// }
+// }
+// }
+//
+// timer.cancel()
+// }
+// }
+// }
+// }
+// }
+// })
+//
+// timer.resume()
+// }
}
-// MARK: - NCLoginProviderDelegate
+extension NCLogin: SFSafariViewControllerDelegate {
+ func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
+ if !loginFlowInProgress {
+ loginButton.isEnabled = true
+ loginButton.hideSpinnerAndShowButton()
+ }
+ }
+}
extension NCLogin: NCLoginProviderDelegate {
func onBack() {
@@ -503,3 +568,7 @@ extension NCLogin: NCLoginProviderDelegate {
loginButton.hideSpinnerAndShowButton()
}
}
+
+protocol NCLoginProviderDelegate: AnyObject {
+ func onBack()
+}
diff --git a/iOSClient/Login/NCLoginProvider.swift b/iOSClient/Login/NCLoginProvider.swift
index f6f20c9eb5..40354d1561 100644
--- a/iOSClient/Login/NCLoginProvider.swift
+++ b/iOSClient/Login/NCLoginProvider.swift
@@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
-// SPDX-FileCopyrightText: 2025 Iva Horn
// SPDX-FileCopyrightText: 2025 Milen Pivchev
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -8,66 +7,55 @@ import WebKit
import NextcloudKit
import FloatingPanel
-protocol NCLoginProviderDelegate: AnyObject {
- ///
- /// Called when the back button is tapped in the login provider view.
- ///
- func onBack()
-}
-
-///
-/// View which presents the web view to login at a Nextcloud instance.
-///
class NCLoginProvider: UIViewController {
+ var logger = NextcloudKit.shared.nkCommonInstance
var webView: WKWebView?
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
let utility = NCUtility()
var titleView: String = ""
- var initialURLString = ""
+ var urlBase = ""
var uiColor: UIColor = .white
+ var pollTimer: DispatchSourceTimer?
weak var delegate: NCLoginProviderDelegate?
var controller: NCMainTabBarController?
- ///
- /// A polling loop active in the background to check for the current status of the login flow.
- ///
- var pollingTask: Task?
-
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
- nkLog(debug: "Login provider view did load.")
- let configuration = WKWebViewConfiguration()
- configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
-
- let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
- webView.customUserAgent = userAgent
- webView.isInspectable = true
- webView.navigationDelegate = self
- view.addSubview(webView)
- webView.translatesAutoresizingMaskIntoConstraints = false
- webView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
- webView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
- webView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
- webView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
+ webView = WKWebView(frame: CGRect.zero, configuration: WKWebViewConfiguration())
+ if let webView {
+ webView.navigationDelegate = self
+ view.addSubview(webView)
- self.webView = webView
+ webView.translatesAutoresizingMaskIntoConstraints = false
+ webView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
+ webView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
+ webView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
+ webView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
+ }
- let navigationItemBack = UIBarButtonItem(image: UIImage(systemName: "arrow.left"), style: .plain, target: self, action: #selector(goBack(_:)))
+ let navigationItemBack = UIBarButtonItem(image: UIImage(systemName: "arrow.left"), style: .done, target: self, action: #selector(goBack))
navigationItemBack.tintColor = uiColor
navigationItem.leftBarButtonItem = navigationItemBack
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- nkLog(debug: "Login provider appeared.")
- guard let url = URL(string: initialURLString) else {
+ if let url = URL(string: urlBase),
+ let webView {
+ HTTPCookieStorage.shared.removeCookies(since: Date.distantPast)
+
+ WKWebsiteDataStore.default().fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
+ WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), for: records, completionHandler: {
+ self.loadWebPage(webView: webView, url: url)
+ })
+ }
+ } else {
let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_login_url_error_")
NCContentPresenter().showError(error: error, priority: .max)
- return
}
if #available(iOS 13, *) {
@@ -85,58 +73,46 @@ class NCLoginProvider: UIViewController {
statusBar.backgroundColor = NCBrandColor.shared.customer
}
}
- loadWebPage(url: url)
- self.title = titleView
self.navigationController?.navigationBar.backgroundColor = NCBrandColor.shared.customer
-
- appDelegate.timerErrorNetworkingDisabled = true
+ // Stop timer error network
+// appDelegate.timerErrorNetworkingDisabled = true
+ if let account = NCManageDatabase.shared.getActiveTableAccount(), NCKeychain().getPassword(account: account.account).isEmpty {
+
+ let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
+ let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+ present(alertController, animated: true)
+ }
}
-// override func viewDidAppear(_ animated: Bool) {
-// super.viewDidAppear(animated)
-// // Stop timer error network
-//// appDelegate.timerErrorNetworkingDisabled = true
-// if let account = NCManageDatabase.shared.getActiveTableAccount(), NCKeychain().getPassword(account: account.account).isEmpty {
-//
-// let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
-// let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
-// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
-// present(alertController, animated: true)
-// }
-// }
-
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
- nkLog(debug: "Login provider view did disappear.")
-
NCActivityIndicator.shared.stop()
-
-// guard pollingTask != nil else {
-// return
-// }
-//
-// nkLog(debug: "Cancelling existing polling task because view did disappear...")
-// pollingTask?.cancel()
-// pollingTask = nil
// appDelegate.timerErrorNetworkingDisabled = false
}
- // MARK: - Navigation
-
- private func loadWebPage(url: URL) {
+ func loadWebPage(webView: WKWebView, url: URL) {
let language = NSLocale.preferredLanguages[0] as String
var request = URLRequest(url: url)
+ if let deviceName = "\(UIDevice.current.name) (\(NCBrandOptions.shared.brand) iOS)".cString(using: .utf8),
+ let deviceUserAgent = String(cString: deviceName, encoding: .ascii) {
+ webView.customUserAgent = deviceUserAgent
+ } else {
+ webView.customUserAgent = userAgent
+ }
+
request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
request.addValue(language, forHTTPHeaderField: "Accept-Language")
webView.load(request)
}
- ///
- /// Dismiss the login web view from the hierarchy.
- ///
- @objc func goBack(_ sender: Any?) {
+ @objc func closeView(sender: UIBarButtonItem) {
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func goBack() {
delegate?.onBack()
if isModal {
@@ -146,81 +122,37 @@ class NCLoginProvider: UIViewController {
}
}
- // MARK: - Polling
-
- ///
- /// Start checking the status of the login flow in the background periodally.
- ///
- func startPolling(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) {
- nkLog(start: "Starting polling at \(loginFlowV2Endpoint) with token \(loginFlowV2Token)")
- pollingTask = createPollingTask(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint)
- nkLog(debug: "Polling task created.")
- }
-
- ///
- /// Fetch the server response and process it.
- ///
- private func poll(token: String, endpoint: String, options: NKRequestOptions) async -> (urlBase: String, loginName: String, appPassword: String)? {
+ func getPollResponse(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginOptions: NKRequestOptions) async -> (urlBase: String, loginName: String, appPassword: String)? {
await withCheckedContinuation { continuation in
- NextcloudKit.shared.getLoginFlowV2Poll(token: token, endpoint: endpoint, options: options) {server, loginName, appPassword, _, error in
-
- guard error == .success else {
- nkLog(error: "Login poll result for token \"\(token)\" is not successful!")
- continuation.resume(returning: nil)
- return
- }
-
- guard let urlBase = server else {
- nkLog(error: "Login poll response field for server for token \"\(token)\" is nil!")
- continuation.resume(returning: nil)
- return
- }
-
- guard let user = loginName else {
- nkLog(error: "Login poll response field for user name for token \"\(token)\" is nil!")
+ NextcloudKit.shared.getLoginFlowV2Poll(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint, options: loginOptions) { server, loginName, appPassword, _, error in
+ if error == .success, let urlBase = server, let user = loginName, let appPassword {
+ continuation.resume(returning: (urlBase, user, appPassword))
+ } else {
continuation.resume(returning: nil)
- return
}
-
- guard let appPassword = appPassword else {
- nkLog(error: "Login poll response field for app password for token \"\(token)\" is nil!")
- continuation.resume(returning: nil)
- return
- }
-
- nkLog(debug: "Returning login poll response for \"\(user)\" on \"\(urlBase)\" for token \"\(token)\".")
- continuation.resume(returning: (urlBase, user, appPassword))
}
}
}
- ///
- /// Handle the values acquired by polling successfully.
- ///
- private func handleGrant(urlBase: String, loginName: String, appPassword: String) async {
- nkLog(debug: "Handling login grant values for \(loginName) on \(urlBase)")
+ func handleGrant(urlBase: String, loginName: String, appPassword: String) async {
+ await withCheckedContinuation { continuation in
+ if self.controller == nil {
+ self.controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+ }
- if controller == nil {
- nkLog(debug: "View controller is still undefined, will resolve root view controller of first window.")
- controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+ NCAccount().createAccount(viewController: self, urlBase: urlBase, user: loginName, password: appPassword, controller: controller) {
+ continuation.resume()
+ }
}
-
- await NCAccount().createAccount(viewController: self, urlBase: urlBase, user: loginName, password: appPassword, controller: controller)
- nkLog(debug: "Account creation for \(loginName) on \(urlBase) completed based on login grant values.")
}
- ///
- /// Set up the `Task` which frequently checks the server.
- ///
- private func createPollingTask(token: String, endpoint: String) -> Task {
- let options = NKRequestOptions(customUserAgent: userAgent)
+ func poll(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) {
+ let loginOptions = NKRequestOptions(customUserAgent: userAgent)
var grantValues: (urlBase: String, loginName: String, appPassword: String)?
- return Task { @MainActor in
+ Task { @MainActor in
repeat {
- try Task.checkCancellation()
-
- grantValues = await poll(token: token, endpoint: endpoint, options: options)
+ grantValues = await getPollResponse(loginFlowV2Token: loginFlowV2Token, loginFlowV2Endpoint: loginFlowV2Endpoint, loginOptions: loginOptions)
try await Task.sleep(nanoseconds: 1_000_000_000) // .seconds() is not supported on iOS 15 yet.
} while grantValues == nil
@@ -229,48 +161,32 @@ class NCLoginProvider: UIViewController {
}
await handleGrant(urlBase: grantValues.urlBase, loginName: grantValues.loginName, appPassword: grantValues.appPassword)
- nkLog(debug: "Polling task completed.")
}
}
}
-// MARK: - WKNavigationDelegate
-
extension NCLoginProvider: WKNavigationDelegate {
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
- nkLog(debug: "Web view did receive server redirect for provisional navigation.")
-
- guard let currentWebViewURL = webView.url else {
- nkLog(error: "Web view does not have a URL after receiving a server redirect for provisional navigation!")
- return
- }
-
- let currentWebViewURLString: String = currentWebViewURL.absoluteString.lowercased()
-
- // Prevent HTTP redirects.
- if initialURLString.lowercased().hasPrefix("https://") && currentWebViewURLString.hasPrefix("http://") {
- nkLog(error: "Web view redirect degrades session from HTTPS to HTTP and must be cancelled!")
+ guard let url = webView.url else { return }
+ let urlString: String = url.absoluteString.lowercased()
+ // prevent http redirection
+ if urlBase.lowercased().hasPrefix("https://") && urlString.lowercased().hasPrefix("http://") {
let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_prevent_http_redirection_", comment: ""), preferredStyle: .alert)
-
alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in
_ = self.navigationController?.popViewController(animated: true)
}))
-
self.present(alertController, animated: true)
-
return
}
- // Login via provider.
- if currentWebViewURLString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol) && currentWebViewURLString.contains("login") {
- nkLog(debug: "Web view redirect to provider login URL detected.")
-
+ // Login via provider
+ if urlString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol) == true && urlString.contains("login") == true {
var server: String = ""
var user: String = ""
var password: String = ""
- let keyValue = currentWebViewURL.path.components(separatedBy: "&")
+ let keyValue = url.path.components(separatedBy: "&")
for value in keyValue {
if value.contains("server:") { server = value }
if value.contains("user:") { user = value }
@@ -286,16 +202,12 @@ extension NCLoginProvider: WKNavigationDelegate {
self.controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
}
- Task { @MainActor in
- await NCAccount().createAccount(viewController: self, urlBase: server, user: username, password: password, controller: controller)
- }
+ NCAccount().createAccount(viewController: self, urlBase: server, user: username, password: password, controller: controller)
}
}
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
- nkLog(debug: "Web view did receive authentication challenge.")
-
DispatchQueue.global().async {
if let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
@@ -306,17 +218,14 @@ extension NCLoginProvider: WKNavigationDelegate {
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
- nkLog(debug: "Web view will allow navigation to \(navigationAction.request.url?.absoluteString ?? "nil")")
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
- nkLog(debug: "Web view did start provisional navigation.")
NCActivityIndicator.shared.startActivity(backgroundView: self.view, style: .medium, blurEffect: false)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
- nkLog(debug: "Web view did finish navigation to \(webView.url?.absoluteString ?? "nil")")
NCActivityIndicator.shared.stop()
}
@@ -328,7 +237,7 @@ extension NCLoginProvider: WKNavigationDelegate {
// let account: String = "\(username) \(urlBase)"
// let user = username
//
-// NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
+//// NextcloudKit.shared.setup(account: account, user: user, userId: user, password: password, urlBase: urlBase)
// NextcloudKit.shared.getUserProfile(account: account) { _, userProfile, _, error in
// if error == .success, let userProfile {
// NextcloudKit.shared.appendSession(account: account,
@@ -343,9 +252,9 @@ extension NCLoginProvider: WKNavigationDelegate {
// httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload,
// groupIdentifier: NCBrandOptions.shared.capabilitiesGroup)
// NCSession.shared.appendSession(account: account, urlBase: urlBase, user: user, userId: userProfile.userId)
-// NCManageDatabase.shared.deleteAccount(account)
+// NCAccount().deleteAccount(account)
// NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password)
-// self.appDelegate.changeAccount(account, userProfile: userProfile) { }
+// NCAccount().changeAccount(account, userProfile: userProfile, controller: nil) { }
// let window = UIApplication.shared.firstWindow
// if window?.rootViewController is NCMainTabBarController {
// self.dismiss(animated: true)
diff --git a/iOSClient/Login/NCLoginWeb.swift b/iOSClient/Login/NCLoginWeb.swift
index daa39f5e6c..9c77f83e49 100644
--- a/iOSClient/Login/NCLoginWeb.swift
+++ b/iOSClient/Login/NCLoginWeb.swift
@@ -49,6 +49,9 @@ class NCLoginWeb: UIViewController {
var loginFlowV2Endpoint = ""
var loginFlowV2Login = ""
+ /// Controller
+ var controller: NCMainTabBarController?
+
// MARK: - View Life Cycle
override func viewDidLoad() {
@@ -329,42 +332,54 @@ extension NCLoginWeb: WKNavigationDelegate {
// MARK: -
- func createAccount(urlBase: String, user: String, password: String) {
- let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+// func createAccount(urlBase: String, user: String, password: String) {
+// let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+// if let host = URL(string: urlBase)?.host {
+// NCNetworking.shared.writeCertificate(host: host)
+// }
+// NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
+// if error == .success {
+// let window = UIApplication.shared.firstWindow
+// if let controller = window?.rootViewController as? NCMainTabBarController {
+// controller.account = account
+// self.dismiss(animated: true)
+// } else {
+// if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
+// AnalyticsHelper.shared.trackUserData()
+// controller.account = account
+// controller.modalPresentationStyle = .fullScreen
+// controller.view.alpha = 0
+//
+// window?.rootViewController = controller
+// window?.makeKeyAndVisible()
+//
+// if let scene = window?.windowScene {
+// SceneManager.shared.register(scene: scene, withRootViewController: controller)
+// }
+//
+// UIView.animate(withDuration: 0.5) {
+// controller.view.alpha = 1
+// }
+// }
+// }
+// } else {
+// let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
+// alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+// self.present(alertController, animated: true)
+// }
+// }
+// }
+
+ private func createAccount(urlBase: String, user: String, password: String) {
+ if self.controller == nil {
+ self.controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController
+ }
+
if let host = URL(string: urlBase)?.host {
NCNetworking.shared.writeCertificate(host: host)
}
- NCAccount().createAccount(urlBase: urlBase, user: user, password: password, controller: controller) { account, error in
- if error == .success {
- let window = UIApplication.shared.firstWindow
- if let controller = window?.rootViewController as? NCMainTabBarController {
- controller.account = account
- self.dismiss(animated: true)
- } else {
- if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController {
- AnalyticsHelper.shared.trackUserData()
- controller.account = account
- controller.modalPresentationStyle = .fullScreen
- controller.view.alpha = 0
-
- window?.rootViewController = controller
- window?.makeKeyAndVisible()
-
- if let scene = window?.windowScene {
- SceneManager.shared.register(scene: scene, withRootViewController: controller)
- }
-
- UIView.animate(withDuration: 0.5) {
- controller.view.alpha = 1
- }
- }
- }
- } else {
- let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: error.errorDescription, preferredStyle: .alert)
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
- self.present(alertController, animated: true)
- }
- }
+
+ NCAccount().createAccount(viewController: self, urlBase: urlBase, user: user, password: password, controller: self.controller)
}
}
diff --git a/iOSClient/Login/NCViewCertificateDetails.storyboard b/iOSClient/Login/NCViewCertificateDetails.storyboard
index f19b796397..53d6dc40ea 100644
--- a/iOSClient/Login/NCViewCertificateDetails.storyboard
+++ b/iOSClient/Login/NCViewCertificateDetails.storyboard
@@ -1,8 +1,9 @@
-
+
-
+
+
@@ -19,17 +20,11 @@
-
-
-
-
-
-
-
+
-
+
@@ -51,12 +46,9 @@
-
-
+
-
-
@@ -68,7 +60,6 @@
-
@@ -83,7 +74,7 @@
-
+
From 16cd02ca1ec1fece4b73ee723fc71e075a1a193f Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Tue, 17 Oct 2023 18:03:55 +0530
Subject: [PATCH 043/177] NMC 1971 - Launch screen and Intro screen UI
Customisation
---
Brand/Intro/NCIntro.storyboard | 134 +++++++--------
Brand/Intro/NCIntroCollectionViewCell.xib | 43 ++---
Brand/Intro/NCIntroViewController.swift | 83 +++++++--
Brand/LaunchScreen.storyboard | 33 +++-
.../OnboardingTestCase.swift | 158 ++++++++++++++++++
iOSClient/AppDelegate.swift | 9 +
iOSClient/AppUtility.swift | 21 +++
7 files changed, 362 insertions(+), 119 deletions(-)
create mode 100644 Tests/NextcloudUnitTests/OnboardingTestCase.swift
create mode 100644 iOSClient/AppUtility.swift
diff --git a/Brand/Intro/NCIntro.storyboard b/Brand/Intro/NCIntro.storyboard
index 1a4fabd425..c17b6df339 100644
--- a/Brand/Intro/NCIntro.storyboard
+++ b/Brand/Intro/NCIntro.storyboard
@@ -1,137 +1,123 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
-
-
+
+
-
-
+
-
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
diff --git a/Brand/Intro/NCIntroCollectionViewCell.xib b/Brand/Intro/NCIntroCollectionViewCell.xib
index 7eed66d3ba..ab7c53a1de 100644
--- a/Brand/Intro/NCIntroCollectionViewCell.xib
+++ b/Brand/Intro/NCIntroCollectionViewCell.xib
@@ -1,56 +1,57 @@
-
+
-
+
-
+
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
-
+
diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift
index c626a4c557..e0ceaf6d3c 100644
--- a/Brand/Intro/NCIntroViewController.swift
+++ b/Brand/Intro/NCIntroViewController.swift
@@ -4,6 +4,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import UIKit
+import NextcloudKit
class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var buttonLogin: UIButton!
@@ -11,22 +12,30 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
@IBOutlet weak var buttonHost: UIButton!
@IBOutlet weak var introCollectionView: UICollectionView!
@IBOutlet weak var pageControl: UIPageControl!
+ @IBOutlet weak var contstraintBottomLoginButton: NSLayoutConstraint!
weak var delegate: NCIntroViewController?
// Controller
var controller: NCMainTabBarController?
private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
- private let titles = [NSLocalizedString("_intro_1_title_", comment: ""), NSLocalizedString("_intro_2_title_", comment: ""), NSLocalizedString("_intro_3_title_", comment: ""), NSLocalizedString("_intro_4_title_", comment: "")]
- private let images = [UIImage(named: "intro1"), UIImage(named: "intro2"), UIImage(named: "intro3"), UIImage(named: "intro4")]
- private var timer: Timer?
+ private let titles = [NSLocalizedString("", comment: ""), NSLocalizedString("", comment: ""), NSLocalizedString("", comment: "")]
+ private var images:[UIImage?] = []
+ private var timerAutoScroll: Timer?
+
private var textColor: UIColor = .white
private var textColorOpponent: UIColor = .black
+ private let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")]
+ private let imagesPortrait = [UIImage(named: "introSlide1"), UIImage(named: "introSlide2"), UIImage(named: "introSlide3")]
+ private let imagesEightPortrait = [UIImage(named: "introSlideEight1"), UIImage(named: "introSlideEight2"), UIImage(named: "introSlideEight3")]
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
+
+ let isEightPlusDevice = UIScreen.main.bounds.height == 736
+ images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait)
let isTooLight = NCBrandColor.shared.customer.isTooLight()
let isTooDark = NCBrandColor.shared.customer.isTooDark()
@@ -82,7 +91,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
view.backgroundColor = NCBrandColor.shared.customer
- self.timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(self.autoScroll(_:))), userInfo: nil, repeats: true)
+ timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true)
+ NotificationCenter.default.addObserver(self, selector: #selector(resetPageController(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
@@ -93,20 +103,38 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
}
}
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ if (UIDevice.current.userInterfaceIdiom != .pad){
+ AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
+ }
+ navigationController?.setNavigationBarHidden(true, animated: animated)
+ }
+
+ override func viewDidLayoutSubviews() {
+ if UIScreen.main.bounds.width < 350 || UIScreen.main.bounds.height > 800 {
+ contstraintBottomLoginButton.constant = 15
+ }
+ }
+
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
-
- timer?.invalidate()
- timer = nil
+ timerAutoScroll?.invalidate()
+ AppUtility.lockOrientation(UIInterfaceOrientationMask.all)
+ navigationController?.setNavigationBarHidden(false, animated: animated)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
- super.viewWillTransition(to: size, with: coordinator)
-
- coordinator.animate(alongsideTransition: nil) { _ in
- self.pageControl?.currentPage = 0
- self.introCollectionView?.collectionViewLayout.invalidateLayout()
- }
+ let isEightPlusDevice = UIScreen.main.bounds.height == 736
+ images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait)
+ pageControl.currentPage = 0
+ introCollectionView.collectionViewLayout.invalidateLayout()
+ self.introCollectionView.reloadData()
+ }
+
+ @objc func resetPageController(_ notification: NSNotification){
+ pageControl.currentPage = 0
+ introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: true)
}
@objc func autoScroll(_ sender: Any?) {
@@ -133,6 +161,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
cell.titleLabel.textColor = textColor
cell.titleLabel.text = titles[indexPath.row]
cell.imageView.image = images[indexPath.row]
+ cell.imageView.contentMode = .scaleAspectFill
return cell
}
@@ -141,8 +170,14 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
- timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: (#selector(autoScroll(_:))), userInfo: nil, repeats: true)
- pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
+ timerAutoScroll = Timer.scheduledTimer(timeInterval: 5, target: self, selector: (#selector(NCIntroViewController.autoScroll)), userInfo: nil, repeats: true)
+ let page = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
+ if pageControl.currentPage == (images.count - 1), pageControl.currentPage <= page {
+ pageControl.currentPage = 0
+ introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: false)
+ } else {
+ pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
+ }
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
@@ -157,11 +192,23 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
}
@IBAction func login(_ sender: Any) {
- if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin {
- viewController.controller = self.controller
- self.navigationController?.pushViewController(viewController, animated: true)
+ if NCBrandOptions.shared.use_AppConfig {
+ let loginViewPage = UIStoryboard(name: "NCLogin", bundle: Bundle.main).instantiateViewController(identifier: "NCLogin")
+ navigationController?.pushViewController(loginViewPage, animated: true)
+ } else {
+ if NextcloudKit.shared.isNetworkReachable() {
+ appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
+ } else {
+ showNoInternetAlert()
+ }
}
}
+
+ func showNoInternetAlert(){
+ let alertController = UIAlertController(title: NSLocalizedString("_no_internet_alert_title_", comment: ""), message: NSLocalizedString("_no_internet_alert_message_", comment: ""), preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { action in }))
+ self.present(alertController, animated: true)
+ }
@IBAction func signupWithProvider(_ sender: Any) {
if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider {
diff --git a/Brand/LaunchScreen.storyboard b/Brand/LaunchScreen.storyboard
index 26840f6195..29059fe902 100755
--- a/Brand/LaunchScreen.storyboard
+++ b/Brand/LaunchScreen.storyboard
@@ -1,9 +1,9 @@
-
+
-
+
@@ -17,16 +17,37 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
+
+
diff --git a/Tests/NextcloudUnitTests/OnboardingTestCase.swift b/Tests/NextcloudUnitTests/OnboardingTestCase.swift
new file mode 100644
index 0000000000..161b3a9258
--- /dev/null
+++ b/Tests/NextcloudUnitTests/OnboardingTestCase.swift
@@ -0,0 +1,158 @@
+//
+// OnboardingTestCase.swift
+// NextcloudTests
+//
+// Created by A200073704 on 21/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+ class OnboardingTestCase: XCTestCase {
+
+ var viewController = NCIntroViewController()
+
+
+ var images:[UIImage?] = []
+ let imagesLandscape = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")]
+ let imagesPortrait = [UIImage(named: "introSlide1"), UIImage(named: "introSlide2"), UIImage(named: "introSlide3")]
+ let imagesEightPortrait = [UIImage(named: "introSlideEight1"), UIImage(named: "introSlideEight2"), UIImage(named: "introSlideEight3")]
+
+
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+
+ func testValidImage() {
+
+ // onscreen images should not be nill
+ let image = [UIImage(named: "introSlideLand1"), UIImage(named: "introSlideLand2"), UIImage(named: "introSlideLand3")]
+ XCTAssertNotNil(image, "Image should not be nil")
+
+ }
+
+ func testImageDimensionsLandscape() {
+
+ // testing height and width of the image
+ let introCollectionView = UIImage(named: "introSlideLand1")
+ XCTAssertEqual(introCollectionView?.size.width, 390, "Image width should be 390")
+ XCTAssertEqual(introCollectionView?.size.height, 844.3333333333334, "Image height should be 844.3333333333334")
+ }
+
+ func testImageDimensionsPortrait() {
+
+ // testing height and width of the image
+ let introCollectionView = UIImage(named: "introSlide1")
+
+ XCTAssertEqual(introCollectionView?.size.width, 390, "Image width should be 390")
+ XCTAssertEqual(introCollectionView?.size.height, 844.3333333333334, "Image height should be 844.3333333333334")
+ }
+
+
+ func testImageDimentionsNotEqual() {
+
+ // testing width and height if not equal
+ let introCollectionView = UIImage(named: "introSlide2")
+
+ XCTAssertNotEqual(introCollectionView?.size.width, 100, "Image width should be 390")
+ XCTAssertNotEqual(introCollectionView?.size.height, 820, "Image height should be 844.3333333333334")
+
+ }
+
+
+ func testImageContentMode() {
+
+ // imageview content mode should be scaleAspectFill
+ let imageView = UIImageView()
+ imageView.contentMode = .scaleAspectFill
+ imageView.image = UIImage(named: "introSlideLand2")
+ XCTAssertEqual(imageView.contentMode, .scaleAspectFill, "Image content mode should be scaleAspectFill")
+
+ }
+
+
+ // Background color of view should be customer
+ func testBackgroundcolor() {
+
+ let backgroundColor = NCBrandColor.shared.customer
+ XCTAssertNotNil(backgroundColor, "NCBrandColor.shared.customer should not be nil")
+
+ }
+
+
+ // Button login text color shouyld be white
+ func testButtonLoginTextColor() {
+
+ let textColor: UIColor = .white
+ viewController.buttonLogin?.backgroundColor = textColor
+
+ XCTAssertEqual(textColor, textColor)
+
+ }
+
+ // images at loginscreen should not be empty
+ func testImagesNotEmpty() {
+
+ let isEightPlusDevice = UIScreen.main.bounds.height == 736
+ images = UIDevice.current.orientation.isLandscape ? imagesLandscape : (isEightPlusDevice ? imagesEightPortrait : imagesPortrait)
+
+ XCTAssertFalse(images.isEmpty)
+ }
+
+
+ // Status bar and navigation bar color should not be blue color
+ func testStatueBarColorNotEqualToCustomer() {
+
+
+ let view = NCLoginWeb()
+ var color = view.navigationController?.navigationBar.backgroundColor
+ let navigationBarColor: UIColor = NCBrandColor.shared.customer
+ color = .systemBlue
+
+ XCTAssertNotEqual(navigationBarColor, color)
+
+ }
+
+ //NavigationBar and status Bar color should be equal
+ func testNavigationBarColorEqualToCustomer() {
+
+ let statusBarColor = NCBrandColor.shared.customer
+ let navigationBarColor: UIColor = NCBrandColor.shared.customer
+
+ XCTAssertEqual(navigationBarColor, statusBarColor)
+ }
+
+ func testEightPlusDeviceHeight() {
+
+ let eightPlusDevice = UIScreen.main.bounds.height >= 736
+
+ XCTAssertTrue(eightPlusDevice)
+
+ }
+
+ func testLoginButtonTapped() {
+
+ let viewController = NCIntroViewController()
+
+ let loginButton = UIButton()
+ loginButton.addTarget(nil, action: #selector(viewController.login(_:)), for: .touchUpInside)
+ loginButton.sendActions(for: .touchUpInside)
+
+ viewController.login(loginButton)
+
+ XCTAssertNotNil(loginButton)
+ }
+
+
+
+
+}
diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift
index 53087a9f8d..c25e0b1edc 100644
--- a/iOSClient/AppDelegate.swift
+++ b/iOSClient/AppDelegate.swift
@@ -17,6 +17,8 @@ import RealmSwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var backgroundSessionCompletionHandler: (() -> Void)?
+ var taskAutoUploadDate: Date = Date()
+ var orientationLock = UIInterfaceOrientationMask.all
var isUiTestingEnabled: Bool {
return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
}
@@ -483,3 +485,10 @@ extension AppDelegate: NCCreateFormUploadConflictDelegate {
}
}
}
+
+//MARK: NMC Customisation
+extension AppDelegate {
+ func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
+ return self.orientationLock
+ }
+}
diff --git a/iOSClient/AppUtility.swift b/iOSClient/AppUtility.swift
new file mode 100644
index 0000000000..bb7c625e53
--- /dev/null
+++ b/iOSClient/AppUtility.swift
@@ -0,0 +1,21 @@
+//
+// AppUtility.swift
+// Nextcloud
+//
+// Created by Amrut Waghmare on 17/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+struct AppUtility {
+ static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
+ if let delegate = UIApplication.shared.delegate as? AppDelegate {
+ delegate.orientationLock = orientation
+ }
+ }
+
+ static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
+ self.lockOrientation(orientation)
+ UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
+ }
+}
From 2bc06478bac3a0217e0aa5da055e4caadcefeaa9 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 12:11:56 +0530
Subject: [PATCH 044/177] NMC 1971 - AppDelegate and Intro screen UI
Customisation
---
Brand/Intro/NCIntroViewController.swift | 3 +-
Nextcloud.xcodeproj/project.pbxproj | 11 ++
iOSClient/AppDelegate.swift | 193 +++++++++++++++++++++++-
3 files changed, 205 insertions(+), 2 deletions(-)
diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift
index e0ceaf6d3c..828bb1bf8d 100644
--- a/Brand/Intro/NCIntroViewController.swift
+++ b/Brand/Intro/NCIntroViewController.swift
@@ -197,7 +197,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
navigationController?.pushViewController(loginViewPage, animated: true)
} else {
if NextcloudKit.shared.isNetworkReachable() {
- appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
+// appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
+ appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
} else {
showNoInternetAlert()
}
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index d3dc9dee77..957953d641 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,10 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B54315322DA64BAF00981E7E /* OnboardingTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */; };
+ B54315342DA64D6500981E7E /* AppUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315332DA64D6500981E7E /* AppUtility.swift */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1368,6 +1372,9 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTestCase.swift; sourceTree = ""; };
+ B54315332DA64D6500981E7E /* AppUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUtility.swift; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2173,6 +2180,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B54315312DA64BAF00981E7E /* OnboardingTestCase.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -3357,6 +3366,7 @@
isa = PBXGroup;
children = (
AA517BB42D66149900F8D37C /* .tx */,
+ B54315332DA64D6500981E7E /* AppUtility.swift */,
F702F2CC25EE5B4F008F8E80 /* AppDelegate.swift */,
F794E13E2BBC0F70003693D7 /* SceneDelegate.swift */,
F7CF067A2E0FF38F0063AD04 /* NCAppStateManager.swift */,
@@ -4854,6 +4864,7 @@
F7FA80002C0F4F3B0072FC60 /* NCUploadAssetsModel.swift in Sources */,
F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */,
F73EF7DF2B02266D0087E6E9 /* NCManageDatabase+Trash.swift in Sources */,
+ B54315342DA64D6500981E7E /* AppUtility.swift in Sources */,
F79B646026CA661600838ACA /* UIControl+Extension.swift in Sources */,
F768823E2C0DD305001CF441 /* LazyView.swift in Sources */,
F3E173B02C9AF637006D177A /* ScreenAwakeManager.swift in Sources */,
diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift
index c25e0b1edc..b70823b22e 100644
--- a/iOSClient/AppDelegate.swift
+++ b/iOSClient/AppDelegate.swift
@@ -17,8 +17,11 @@ import RealmSwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var backgroundSessionCompletionHandler: (() -> Void)?
+ var activeLogin: NCLogin?
+ var activeLoginWeb: NCLoginWeb?
var taskAutoUploadDate: Date = Date()
var orientationLock = UIInterfaceOrientationMask.all
+ @objc let adjust = AdjustHelper()
var isUiTestingEnabled: Bool {
return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
}
@@ -33,6 +36,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
var pushSubscriptionTask: Task?
+ var window: UIWindow?
+ @objc var sceneIdentifier: String = ""
+ @objc var activeViewController: UIViewController?
+ @objc var account: String = ""
+ @objc var urlBase: String = ""
+ @objc var user: String = ""
+ @objc var userId: String = ""
+ @objc var password: String = ""
+ var timerErrorNetworking: Timer?
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if isUiTestingEnabled {
Task {
@@ -60,6 +73,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
#endif
NCBrandColor.shared.createUserColors()
+ NCImageCache.shared.createImagesCache()
NextcloudKit.shared.setup(groupIdentifier: NCBrandOptions.shared.capabilitiesGroup,
delegate: NCNetworking.shared)
@@ -118,6 +132,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
NCPreferences().requestPasscodeAtStart = true
}
+ /// Activation singleton
+ _ = NCAppStateManager.shared
+ _ = NCNetworking.shared
+ _ = NCDownloadAction.shared
+ _ = NCNetworkingProcess.shared
+
+ adjust.configAdjust()
+ adjust.subsessionStart()
+ TealiumHelper.shared.start()
+ FirebaseApp.configure()
+
return true
}
@@ -405,6 +430,151 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
+ // MARK: - Login
+
+ func openLogin(selector: Int, window: UIWindow? = nil) {
+ UIApplication.shared.allSceneSessionDestructionExceptFirst()
+
+ // Nextcloud standard login
+ if selector == NCGlobal.shared.introSignup {
+ if activeLogin?.view.window == nil {
+ if selector == NCGlobal.shared.introSignup {
+ let web = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider
+ web?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders
+ showLoginViewController(web)
+ } else {
+ activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
+ if let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController, !controller.account.isEmpty {
+ let session = NCSession.shared.getSession(account: controller.account)
+ activeLogin?.urlBase = session.urlBase
+ }
+ showLoginViewController(activeLogin)
+ }
+ }
+ } else {
+ if activeLogin?.view.window == nil {
+ activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
+ activeLogin?.urlBase = NCBrandOptions.shared.disable_request_login_url ? NCBrandOptions.shared.loginBaseUrl : ""
+ showLoginViewController(activeLogin)
+ }
+ }
+ }
+
+ @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) {
+// openLogin(selector: NCGlobal.shared.introLogin)
+ // [WEBPersonalized] [AppConfig]
+ if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {
+
+ if activeLoginWeb?.view.window == nil {
+ activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
+ activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
+ showLoginViewController(activeLoginWeb, contextViewController: viewController)
+ }
+ return
+ }
+
+ // Nextcloud standard login
+ if selector == NCGlobal.shared.introSignup {
+
+ if activeLoginWeb?.view.window == nil {
+ activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
+ if selector == NCGlobal.shared.introSignup {
+ activeLoginWeb?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders
+ } else {
+ activeLoginWeb?.urlBase = self.urlBase
+ }
+ showLoginViewController(activeLoginWeb, contextViewController: viewController)
+ }
+
+ } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {
+
+ if activeLoginWeb?.view.window == nil {
+ activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
+ activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
+ showLoginViewController(activeLoginWeb, contextViewController: viewController)
+ }
+
+ } else if openLoginWeb {
+
+ // Used also for reinsert the account (change passwd)
+ if activeLoginWeb?.view.window == nil {
+ activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
+ activeLoginWeb?.urlBase = urlBase
+ activeLoginWeb?.user = user
+ showLoginViewController(activeLoginWeb, contextViewController: viewController)
+ }
+
+ } else {
+
+ if activeLogin?.view.window == nil {
+ activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
+ showLoginViewController(activeLogin, contextViewController: viewController)
+ }
+ }
+ }
+
+ func showLoginViewController(_ viewController: UIViewController?) {
+ guard let viewController else { return }
+ let navigationController = NCLoginNavigationController(rootViewController: viewController)
+
+ navigationController.modalPresentationStyle = .fullScreen
+ navigationController.navigationBar.barStyle = .black
+ navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
+ navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
+ navigationController.navigationBar.isTranslucent = false
+
+ if let controller = UIApplication.shared.firstWindow?.rootViewController {
+ if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) {
+ presentedVC.dismiss(animated: false) {
+ controller.present(navigationController, animated: true)
+ }
+ } else {
+ controller.present(navigationController, animated: true)
+ }
+ } else {
+ window?.rootViewController = navigationController
+ window?.makeKeyAndVisible()
+ }
+ }
+
+ func showLoginViewController(_ viewController: UIViewController?, contextViewController: UIViewController?) {
+
+ if contextViewController == nil {
+ if let viewController = viewController {
+ let navigationController = NCLoginNavigationController(rootViewController: viewController)
+ navigationController.navigationBar.barStyle = .black
+ navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
+ navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
+ navigationController.navigationBar.isTranslucent = false
+ window?.rootViewController = navigationController
+ window?.makeKeyAndVisible()
+ }
+ } else if contextViewController is UINavigationController {
+ if let contextViewController = contextViewController, let viewController = viewController {
+ (contextViewController as? UINavigationController)?.pushViewController(viewController, animated: true)
+ }
+ } else {
+ if let viewController = viewController, let contextViewController = contextViewController {
+ let navigationController = NCLoginNavigationController(rootViewController: viewController)
+ navigationController.modalPresentationStyle = .fullScreen
+ navigationController.navigationBar.barStyle = .black
+ navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
+ navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
+ navigationController.navigationBar.isTranslucent = false
+ contextViewController.present(navigationController, animated: true) { }
+ }
+ }
+ }
+
+ @objc func startTimerErrorNetworking() {
+ timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true)
+ }
+
+ @objc private func checkErrorNetworking() {
+ guard !account.isEmpty, NCKeychain().getPassword(account: account).isEmpty else { return }
+ openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
+ }
+
// MARK: -
func trustCertificateError(host: String) {
@@ -441,9 +611,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
}
+ // MARK: - Account
+
+ @objc func changeAccount(_ account: String, userProfile: NKUserProfile?) {
+// NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser)
+ }
+
+ @objc func deleteAccount(_ account: String, wipe: Bool) {
+ NCAccount().deleteAccount(account, wipe: wipe)
+ }
+
+ func deleteAllAccounts() {
+ let accounts = NCManageDatabase.shared.getAccounts()
+ accounts?.forEach({ account in
+ deleteAccount(account, wipe: true)
+ })
+ }
+
+ func updateShareAccounts() -> Error? {
+ return NCAccount().updateAppsShareAccounts()
+ }
+
// MARK: - Reset Application
- func resetApplication() {
+ @objc func resetApplication() {
let utilityFileSystem = NCUtilityFileSystem()
NCNetworking.shared.cancelAllTask()
From 0463739c7436dd873b95f5d7ed5bf8b85f6fee1b Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 21:27:11 +0530
Subject: [PATCH 045/177] NMC 1971 - AppDelegate and Intro screen UI
Customisation
---
Brand/Intro/NCIntroViewController.swift | 28 +--
Brand/LaunchScreen.storyboard | 2 +-
iOSClient/AppDelegate.swift | 236 ++++++++++++------------
3 files changed, 136 insertions(+), 130 deletions(-)
diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift
index 828bb1bf8d..d4ee917c6f 100644
--- a/Brand/Intro/NCIntroViewController.swift
+++ b/Brand/Intro/NCIntroViewController.swift
@@ -60,7 +60,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
self.navigationController?.navigationBar.tintColor = textColor
if !NCManageDatabase.shared.getAllTableAccount().isEmpty {
- let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(actionCancel(_:)))
+ let navigationItemCancel = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(self.actionCancel))
navigationItemCancel.tintColor = textColor
navigationItem.leftBarButtonItem = navigationItemCancel
}
@@ -73,10 +73,13 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
buttonLogin.backgroundColor = textColor
buttonLogin.setTitle(NSLocalizedString("_log_in_", comment: ""), for: .normal)
- buttonSignUp.layer.cornerRadius = 8
+ buttonSignUp.layer.cornerRadius = 20
+ buttonSignUp.layer.borderColor = textColor.cgColor
+ buttonSignUp.layer.borderWidth = 1.0
buttonSignUp.setTitleColor(textColor, for: .normal)
- buttonSignUp.backgroundColor = textColor.withAlphaComponent(0.2)
+ buttonSignUp.backgroundColor = NCBrandColor.shared.customer
buttonSignUp.titleLabel?.adjustsFontSizeToFitWidth = true
+ buttonSignUp.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
buttonSignUp.setTitle(NSLocalizedString("_sign_up_", comment: ""), for: .normal)
buttonHost.layer.cornerRadius = 20
@@ -137,7 +140,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
introCollectionView.scrollToItem(at: IndexPath(row: pageControl.currentPage, section: 0), at: .centeredHorizontally, animated: true)
}
- @objc func autoScroll(_ sender: Any?) {
+ @objc func autoScroll() {
if pageControl.currentPage + 1 >= titles.count {
pageControl.currentPage = 0
} else {
@@ -181,13 +184,13 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
- timer?.invalidate()
- timer = nil
+ timerAutoScroll?.invalidate()
+ timerAutoScroll = nil
}
// MARK: - Action
- @objc func actionCancel(_ sender: Any?) {
+ @objc func actionCancel() {
dismiss(animated: true) { }
}
@@ -197,7 +200,6 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
navigationController?.pushViewController(loginViewPage, animated: true)
} else {
if NextcloudKit.shared.isNetworkReachable() {
-// appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
appDelegate.openLogin(viewController: navigationController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
} else {
showNoInternetAlert()
@@ -212,11 +214,11 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol
}
@IBAction func signupWithProvider(_ sender: Any) {
- if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider {
- viewController.controller = self.controller
- viewController.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders
- self.navigationController?.pushViewController(viewController, animated: true)
- }
+ appDelegate.openLogin(selector: NCGlobal.shared.introSignup)
+ }
+
+ @IBAction func signup(_ sender: Any) {
+ appDelegate.openLogin(selector: NCGlobal.shared.introSignup)
}
@IBAction func host(_ sender: Any) {
diff --git a/Brand/LaunchScreen.storyboard b/Brand/LaunchScreen.storyboard
index 29059fe902..59fe1c3dd1 100755
--- a/Brand/LaunchScreen.storyboard
+++ b/Brand/LaunchScreen.storyboard
@@ -42,7 +42,7 @@
-
+
diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift
index b70823b22e..bc856ceb56 100644
--- a/iOSClient/AppDelegate.swift
+++ b/iOSClient/AppDelegate.swift
@@ -12,7 +12,7 @@ import WidgetKit
import Queuer
import EasyTipView
import SwiftUI
-import RealmSwift
+import MoEngageInApps
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
@@ -26,16 +26,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
return ProcessInfo.processInfo.arguments.contains("UI_TESTING")
}
var notificationSettings: UNNotificationSettings?
+ var pushKitToken: String?
var loginFlowV2Token = ""
var loginFlowV2Endpoint = ""
var loginFlowV2Login = ""
- let backgroundQueue = DispatchQueue(label: "com.nextcloud.bgTaskQueue")
- let global = NCGlobal.shared
-
- var pushSubscriptionTask: Task?
-
+ let database = NCManageDatabase.shared
var window: UIWindow?
@objc var sceneIdentifier: String = ""
@objc var activeViewController: UIViewController?
@@ -45,32 +42,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
@objc var userId: String = ""
@objc var password: String = ""
var timerErrorNetworking: Timer?
-
+ var tipView: EasyTipView?
+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if isUiTestingEnabled {
- Task {
- await NCAccount().deleteAllAccounts()
- }
+ NCAccount().deleteAllAccounts()
}
+ UINavigationBar.appearance().tintColor = NCBrandColor.shared.customer
+ UIToolbar.appearance().tintColor = NCBrandColor.shared.customer
+
let utilityFileSystem = NCUtilityFileSystem()
let utility = NCUtility()
-
- utilityFileSystem.createDirectoryStandard()
- utilityFileSystem.emptyTemporaryDirectory()
- utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto")
-
- let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionBuild())
+ let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, utility.getVersionApp())
NCAppVersionManager.shared.checkAndUpdateInstallState()
NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0)
UserDefaults.standard.register(defaults: ["UserAgent": userAgent])
-
- #if !DEBUG
- if !NCPreferences().disableCrashservice, !NCBrandOptions.shared.disable_crash_service {
+ if !NCKeychain().disableCrashservice, !NCBrandOptions.shared.disable_crash_service {
FirebaseApp.configure()
}
- #endif
+
+ utilityFileSystem.createDirectoryStandard()
+ utilityFileSystem.emptyTemporaryDirectory()
+ utilityFileSystem.clearCacheDirectory("com.limit-point.LivePhoto")
NCBrandColor.shared.createUserColors()
NCImageCache.shared.createImagesCache()
@@ -78,23 +73,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
NextcloudKit.shared.setup(groupIdentifier: NCBrandOptions.shared.capabilitiesGroup,
delegate: NCNetworking.shared)
- NextcloudKit.configureLogger(logLevel: (NCBrandOptions.shared.disable_log ? .disabled : NCPreferences().log))
-
- #if DEBUG
-// For the tags look NCGlobal LOG TAG
-
-// var black: [String] = []
-// black.append("NETWORKING TASKS")
-// NextcloudKit.configureLoggerBlacklist(blacklist: black)
-
-// var white: [String] = []
-// white.append("SYNC METADATA")
-// NextcloudKit.configureLoggerWhitelist(whitelist: white)
- #endif
-
- nkLog(start: "Start session with level \(NCPreferences().log) " + versionNextcloudiOS)
+ if NCBrandOptions.shared.disable_log {
+ utilityFileSystem.removeFile(atPath: NextcloudKit.shared.nkCommonInstance.filenamePathLog)
+ utilityFileSystem.removeFile(atPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NextcloudKit.shared.nkCommonInstance.filenameLog)
+ } else {
+ NextcloudKit.shared.setupLog(pathLog: utilityFileSystem.directoryGroup,
+ levelLog: NCKeychain().logLevel,
+ copyLogToDocumentDirectory: true)
+ NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start session with level \(NCKeychain().logLevel) " + versionNextcloudiOS)
+ }
- // Push Notification & display notification
+ /// Push Notification & display notification
UNUserNotificationCenter.current().getNotificationSettings { settings in
self.notificationSettings = settings
}
@@ -108,41 +97,49 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
review.showStoreReview()
#endif
- // BACKGROUND TASK
- //
- BGTaskScheduler.shared.register(forTaskWithIdentifier: global.refreshTask, using: backgroundQueue) { task in
- guard let appRefreshTask = task as? BGAppRefreshTask else {
- task.setTaskCompleted(success: false)
- return
- }
- self.handleAppRefresh(appRefreshTask)
+ /// Background task register
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
+ self.handleAppRefresh(task)
}
- scheduleAppRefresh()
-
- BGTaskScheduler.shared.register(forTaskWithIdentifier: global.processingTask, using: backgroundQueue) { task in
- guard let processingTask = task as? BGProcessingTask else {
- task.setTaskCompleted(success: false)
- return
- }
- self.handleProcessingTask(processingTask)
+ BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in
+ self.handleProcessingTask(task)
}
- scheduleAppProcessing()
if NCBrandOptions.shared.enforce_passcode_lock {
- NCPreferences().requestPasscodeAtStart = true
+ NCKeychain().requestPasscodeAtStart = true
}
/// Activation singleton
- _ = NCAppStateManager.shared
_ = NCNetworking.shared
- _ = NCDownloadAction.shared
+ _ = NCActionCenter.shared
_ = NCNetworkingProcess.shared
+ _ = NCTransferProgress.shared
+ _ = NCActionCenter.shared
+ NCTransferProgress.shared.setup()
+ NCActionCenter.shared.setup()
+
+// if account.isEmpty {
+// if NCBrandOptions.shared.disable_intro {
+// openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
+// } else {
+// if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() {
+// let navigationController = NCLoginNavigationController(rootViewController: viewController)
+// window?.rootViewController = navigationController
+// window?.makeKeyAndVisible()
+// }
+// }
+// } else {
+// NCPasscode.shared.presentPasscode(delegate: self) {
+// NCPasscode.shared.enableTouchFaceID()
+// }
+// }
adjust.configAdjust()
adjust.subsessionStart()
TealiumHelper.shared.start()
FirebaseApp.configure()
+
return true
}
@@ -156,7 +153,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
notificationCenter.add(req)
}
- nkLog(debug: "bye bye")
+ NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] bye bye")
}
// MARK: - UISceneSession Lifecycle
@@ -179,14 +176,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
@discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage.
*/
func scheduleAppRefresh() {
- let request = BGAppRefreshTaskRequest(identifier: global.refreshTask)
+ let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask)
request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Refresh after 60 seconds.
-
do {
try BGTaskScheduler.shared.submit(request)
} catch {
- nkLog(tag: self.global.logTagTask, emoji: .error, message: "Refresh task failed to submit request: \(error)")
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Refresh task failed to submit request: \(error)")
}
}
@@ -194,28 +190,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
@discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week.
*/
func scheduleAppProcessing() {
- let request = BGProcessingTaskRequest(identifier: global.processingTask)
+ let request = BGProcessingTaskRequest(identifier: NCGlobal.shared.processingTask)
request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes.
request.requiresNetworkConnectivity = false
request.requiresExternalPower = false
-
do {
try BGTaskScheduler.shared.submit(request)
} catch {
- nkLog(tag: self.global.logTagTask, emoji: .error, message: "Processing task failed to submit request: \(error)")
+ NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Background Processing task failed to submit request: \(error)")
}
}
- func handleAppRefresh(_ task: BGAppRefreshTask) {
- nkLog(tag: self.global.logTagTask, emoji: .start, message: "Start refresh task")
- guard NCManageDatabase.shared.openRealmBackground() else {
- nkLog(tag: self.global.logTagTask, emoji: .error, message: "Failed to open Realm in background")
- task.setTaskCompleted(success: false)
- return
- }
-
- // Schedule next refresh
+ func handleAppRefresh(_ task: BGTask) {
scheduleAppRefresh()
Task {
@@ -227,15 +214,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
- func handleProcessingTask(_ task: BGProcessingTask) {
- nkLog(tag: self.global.logTagTask, emoji: .start, message: "Start processing task")
- guard NCManageDatabase.shared.openRealmBackground() else {
- nkLog(tag: self.global.logTagTask, emoji: .error, message: "Failed to open Realm in background")
- task.setTaskCompleted(success: false)
- return
- }
-
- // Schedule next processing task
+ func handleProcessingTask(_ task: BGTask) {
scheduleAppProcessing()
Task {
@@ -291,7 +270,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
return
}
}
-
+
// Capacity computation
let downloading = allMetadatas.lazy.filter { $0.status == self.global.metadataStatusDownloading }.count
let uploading = allMetadatas.lazy.filter { $0.status == self.global.metadataStatusUploading }.count
@@ -334,6 +313,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
nkLog(tag: self.global.logTagBgSync, emoji: .error, message: "Upload failed \(metadata.fileName) -> \(metadata.serverUrl) [\(err.errorDescription)]")
}
}
+
+ let counter = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND (session == %@ || session == %@) AND status != %d",
+ account,
+ NCNetworking.shared.sessionDownloadBackground,
+ NCNetworking.shared.sessionUploadBackground,
+ NCGlobal.shared.metadataStatusNormal))?.count ?? 0
+ UIApplication.shared.applicationIconBadgeNumber = counter
+
+ NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] \(taskText) completion handle")
+ completion()
}
}
@@ -362,22 +351,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
- if let deviceToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) {
- NCPreferences().deviceTokenPushNotification = deviceToken
- pushSubscriptionTask = Task.detached {
- // Wait bounded time for maintenance to be OFF
- let canProceed = await NCAppStateManager.shared.waitForMaintenanceOffAsync()
- guard canProceed else {
- nkLog(error: "[PUSH] Skipping subscription: maintenance mode still ON after timeout")
- return
- }
-
- try? await Task.sleep(nanoseconds: 1_000_000_000)
-
- let tblAccounts = await NCManageDatabase.shared.getAllTableAccountAsync()
- for tblAccount in tblAccounts {
- await NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase)
- }
+ if let pushKitToken = NCPushNotificationEncryption.shared().string(withDeviceToken: deviceToken) {
+ self.pushKitToken = pushKitToken
+ // https://github.com/nextcloud/talk-ios/issues/691
+ for tblAccount in NCManageDatabase.shared.getAllTableAccount() {
+ subscribingPushNotification(account: tblAccount.account, urlBase: tblAccount.urlBase, user: tblAccount.user)
}
}
}
@@ -388,6 +366,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
+ func subscribingPushNotification(account: String, urlBase: String, user: String) {
+#if !targetEnvironment(simulator)
+ NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: UIApplication.shared.firstWindow?.rootViewController) { error in
+ if error == .success {
+ NCPushNotification.shared.subscribingNextcloudServerPushNotification(account: account, urlBase: urlBase, user: user, pushKitToken: self.pushKitToken)
+ }
+ }
+#endif
+ }
+
func nextcloudPushNotificationAction(data: [String: AnyObject]) {
guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data)
else {
@@ -398,12 +386,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func openNotification(controller: NCMainTabBarController) {
if app == NCGlobal.shared.termsOfServiceName {
- Task {
- await NCNetworking.shared.transferDispatcher.notifyAllDelegatesAsync { delegate in
- try? await Task.sleep(nanoseconds: 500_000_000)
- delegate.transferRequestData(serverUrl: nil)
- }
- }
+ NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterGetServerData, second: 0.5)
} else if let navigationController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? UINavigationController,
let viewController = navigationController.topViewController as? NCNotification {
viewController.modalPresentationStyle = .pageSheet
@@ -416,10 +399,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
if let controller = SceneManager.shared.getControllers().first(where: { $0.account == account }) {
openNotification(controller: controller)
- } else if let tblAccount = NCManageDatabase.shared.getAllTableAccount().first(where: { $0.account == account }),
+ } else if let tableAccount = NCManageDatabase.shared.getAllTableAccount().first(where: { $0.account == account }),
let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController {
- Task { @MainActor in
- await NCAccount().changeAccount(tblAccount.account, userProfile: nil, controller: controller)
+ NCAccount().changeAccount(tableAccount.account, userProfile: nil, controller: controller) {
openNotification(controller: controller)
}
} else {
@@ -435,6 +417,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func openLogin(selector: Int, window: UIWindow? = nil) {
UIApplication.shared.allSceneSessionDestructionExceptFirst()
+// func showLoginViewController(_ viewController: UIViewController?) {
+// guard let viewController else { return }
+// let navigationController = NCLoginNavigationController(rootViewController: viewController)
+//
+// navigationController.modalPresentationStyle = .fullScreen
+// navigationController.navigationBar.barStyle = .black
+// navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
+// navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
+// navigationController.navigationBar.isTranslucent = false
+//
+// if let controller = UIApplication.shared.firstWindow?.rootViewController {
+// if let presentedVC = controller.presentedViewController, !(presentedVC is NCLoginNavigationController) {
+// presentedVC.dismiss(animated: false) {
+// controller.present(navigationController, animated: true)
+// }
+// } else {
+// controller.present(navigationController, animated: true)
+// }
+// } else {
+// window?.rootViewController = navigationController
+// window?.makeKeyAndVisible()
+// }
+// }
+
// Nextcloud standard login
if selector == NCGlobal.shared.introSignup {
if activeLogin?.view.window == nil {
@@ -578,8 +584,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: -
func trustCertificateError(host: String) {
- guard let activeTblAccount = NCManageDatabase.shared.getActiveTableAccount(),
- let currentHost = URL(string: activeTblAccount.urlBase)?.host,
+ guard let activeTableAccount = NCManageDatabase.shared.getActiveTableAccount(),
+ let currentHost = URL(string: activeTableAccount.urlBase)?.host,
let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host,
host != pushNotificationServerProxyHost,
host == currentHost
@@ -610,7 +616,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
UIApplication.shared.firstWindow?.rootViewController?.present(alertController, animated: true)
}
-
+
// MARK: - Account
@objc func changeAccount(_ account: String, userProfile: NKUserProfile?) {
@@ -646,7 +652,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
utilityFileSystem.removeDocumentsDirectory()
utilityFileSystem.removeTemporaryDirectory()
- NCPreferences().removeAll()
+ NCKeychain().removeAll()
+ NCNetworking.shared.removeAllKeyUserDefaultsData(account: nil)
exit(0)
}
@@ -669,11 +676,8 @@ extension AppDelegate: NCViewCertificateDetailsDelegate {
extension AppDelegate: NCCreateFormUploadConflictDelegate {
func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
- if let metadatas {
- Task {
- await NCManageDatabase.shared.addMetadatasAsync(metadatas)
- }
- }
+ guard let metadatas = metadatas, !metadatas.isEmpty else { return }
+ NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas)
}
}
From 34b76019cbacb330e28d30cd842e4633b3a6043d Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 18 Oct 2023 14:58:19 +0530
Subject: [PATCH 046/177] NMC 1984 - Privacy policy customisation added
---
.../dataPrivacy.imageset/Contents.json | 23 ++
.../dataPrivacy.imageset/default copy@2x.png | Bin 0 -> 1262 bytes
.../dataPrivacy.imageset/default copy@3x.png | Bin 0 -> 2037 bytes
.../dataPrivacy.imageset/default copy@4x.png | Bin 0 -> 2624 bytes
.../NCCollectionViewCommon.swift | 21 ++
.../AnalysisDataCollectionSwitch.swift | 34 +++
.../Settings/AnalysisDataCollectionSwitch.xib | 73 ++++++
...InitialPrivacySettingsViewController.swift | 170 +++++++++++++
iOSClient/Settings/NCSettings.storyboard | 236 ++++++++++++++++++
.../PrivacyPolicyViewController.swift | 62 +++++
.../PrivacySettingsViewController.swift | 133 ++++++++++
.../RequiredDataCollectionSwitch.swift | 34 +++
.../Settings/RequiredDataCollectionSwitch.xib | 73 ++++++
.../SaveSettingsCustomButtonCell.swift | 46 ++++
.../Settings/SaveSettingsCustomButtonCell.xib | 46 ++++
15 files changed, 951 insertions(+)
create mode 100644 iOSClient/Images.xcassets/dataPrivacy.imageset/Contents.json
create mode 100644 iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@2x.png
create mode 100644 iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@3x.png
create mode 100644 iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@4x.png
create mode 100644 iOSClient/Settings/AnalysisDataCollectionSwitch.swift
create mode 100644 iOSClient/Settings/AnalysisDataCollectionSwitch.xib
create mode 100644 iOSClient/Settings/InitialPrivacySettingsViewController.swift
create mode 100644 iOSClient/Settings/NCSettings.storyboard
create mode 100644 iOSClient/Settings/PrivacyPolicyViewController.swift
create mode 100644 iOSClient/Settings/PrivacySettingsViewController.swift
create mode 100644 iOSClient/Settings/RequiredDataCollectionSwitch.swift
create mode 100644 iOSClient/Settings/RequiredDataCollectionSwitch.xib
create mode 100644 iOSClient/Settings/SaveSettingsCustomButtonCell.swift
create mode 100644 iOSClient/Settings/SaveSettingsCustomButtonCell.xib
diff --git a/iOSClient/Images.xcassets/dataPrivacy.imageset/Contents.json b/iOSClient/Images.xcassets/dataPrivacy.imageset/Contents.json
new file mode 100644
index 0000000000..4fb0d4c09c
--- /dev/null
+++ b/iOSClient/Images.xcassets/dataPrivacy.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "default copy@2x.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "default copy@3x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "default copy@4x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@2x.png b/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..40c3e7d47f2c2fdbdc87713870b113c36d2bbc07
GIT binary patch
literal 1262
zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NGSV=@dRA>e5naPV4R}{wkIf1Ao
z5ETSbL~s#eB7z#VK@nFX?NU*9iDKMmoppq41lN*(AQ}`zT`5kjAWpQPIDkV2g9?$D
zIHdFYy6Y4tRrgxAhVFLZf$!Z@x6b#Sb8ZdqRlUBx4>qwY(Cae&@bK_d^p=4&4oY#(
z;YzR9d#%YXtW^UPJewj`11oq1{`88%Ub6oZu%^z_&>t0?=Hkd&&H*Ulfk>}IXf=^
zHFF*e($JrbB?~q{@mGla3{)k0LCheqVlTuJ5i?@h!e}w9DJ{~>(Y?`6EzAwHb$$KM
zVhzL&-$U41UBgvGCEnO*NQ?9ZP(z0KAwhBl>n+71>g@m<+rh$8;22FPSC&(`a|@*l
zb|i)cCwFaxj>oE6CUu-|+~qzrFz!oUNH``(fb(`UI1YZzAb)cq_tC!Be}>~V4(FfJR5@$+P2$AQZ2>bM$`k6pySyWly_A8*&^eiBL^
zYrr2aJ%mS%75WJL0PX>^pK$b!XvE|L&6(Z9*$1kgg?;WvrdK&4{53E;93v2*yUw5m
z7RHe#m@5|V!%nj??H&4uUGVN$TxlNCB1e3RA3Rdb?{#q@S55ep55;^{Du4+BCjsR)
zTM~+k&cV84Oh&y1DH-!8Tuf8Q0Rf1ro?!(9M$cNa=3iA@uRodq|6Ac2md
zYw$YRq>*VcjF@hp`uC@MLwL_Tv$r^VQtN0HN1|QewPnsmz5lHI!KE0WSZ{iS9ov;Jc1*$zz#o1U^bhzRl&%fU
zW&0H4O^>hrMg4KvCh=V0f3sU*y4a5Zy`>i9Yp!VG>c8fRHHz2-^sSj{cM04wu~iqc
zQ_vK!16%_!yTuM&Kl$TkLFWC~q`-clZ*P4e>D9`QU?T|rsrY_W9jJnWCxa=0U7bz+
Y2SkK6zRJ=a>Hq)$07*qoM6N<$g2I134*&oF
literal 0
HcmV?d00001
diff --git a/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@3x.png b/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..74d9dbb7dd8e51db1844e2102dff7045c235a1fb
GIT binary patch
literal 2037
zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS?Ur9tkRCodHn{TXFa~#LJLd_y{
zMZ^}8G@*Y&>~7Q(tF1iI^x!h{$e*z+p5dAC#0+a>nR$@4qHI=AG}TQcim5Bg5W|(4
zq+7SH*Zb$3+d1F+J?DG=pL_Xzw)g%1{d_*}@B4TDpL2%{Xfe=HnCjx)#P&uKXRdo`_-@;VxLbTq3Vu!*MCJ=&*Ao7jC7i1>&b3@t6YaAr>
zN5B_tD}o&^B3)*I5M&$#d@)pB3x?((^)>6=B=mfaQEme|7&pKIy1cPQLC#hxI%xZtM*YmjV75QM;MOM?^-sy!oh_8-
z1YTK=vqQ?dJV3??6P5V8bb<
z18rdvOYoMk
zfq7rZaPMmRmmt(Pl(krG#~>DeJ1f==>jGekBQeycv2EU5^;K@Q2*iSQ&$J4ROt50j
zS>^d8m|pCZS_EPV&w+DNEJbtWQ@K?x5L?1Uta4t4mCo~gGq`4VsaznawgKBQ$F&Oi
z$hT6fL!hxaR+z^(Q`$NN%ChzNi_7Ed$4FJyA<)mM7=|ZTe&JY$KwHCWPSw&2#7eDl
zfw(Hx^XkqFtK5gL9(26PFeFDpzNWp-p~d&d*qu*Mya
z_A)-3xu0jrVezqXx2>_y)g_Sa1Aq(qymMAq_)bFH03HT9yJ>4V1a!7~3`}N@?)Zx`
z8~0d&T+f3_6-cW_fWIkp0IuR7Y!k?~Mp0{+YYW=%PYjf{B!S@pUA1xlF^;iIARPlt
z8*O$BqPC6ucX9k(0!iKDMgV;Wl*%}J3@)V_d(4F&)g8PtbId
z$Y(@*B^c4Gk4X}hxvk(^i@(_w5{W^VIKKavRP?mbY0MkrI|a&RE=SicCVzd&VOpEn
z5z$^u|K$;5!R|9?tbn+L(Jr?FsLy4Sn4n99B-Un`l>g=z!j}aU_p3`S$wFOBYysc<
z4hc(bNpZWCEsSnwzHTD>DQv4RFUX9M$;
zE_(WeFZRB%GxN9sZZIhF3*R2+RS;O=h(J0t?Ef
zp&VW=ijW}GwopH2F4s)$WbG*5>vlK%O+s$9|otGN|$vpp?%N@5l57ToCjV4
zQ3r5_5}Quz%Rp;f&OtfGqOJ$}#}wPO3*An8pBXa-<>J$jW|3Ee*MaTciSCf;x@I+)
znI^}7VlImw10DrifoXbn{1g~l=VAxUvt)$O^BR5FsVnX^ytCR~r!dXj2^IJUOCpgG
TJQa3{00000NkvXXu0mjfv1qa|
literal 0
HcmV?d00001
diff --git a/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@4x.png b/iOSClient/Images.xcassets/dataPrivacy.imageset/default copy@4x.png
new file mode 100644
index 0000000000000000000000000000000000000000..682122c3f21e9c196b2df4c47179f97e87b9788b
GIT binary patch
literal 2624
zcmV-G3cvMPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91V4wp41ONa40RR91U;qFB0I4%yP5=N2s!2paRCodHok^?|RTPGKf+#3Y
zFc_SIKtPQX4iU%bMx#d5#5e#NbYWt2;l@Oa!Hq*q+!&2R^3~5tNQk{Px7a_Pn~Wa^m(Y+^X4A0R
zN*X}14}j(wpt0a%!cwo?+d3ljH-yih(3EYVqXuaJ1wR5}>7hhLSMi62CW}99sh@)n
z6D(jUIUk&t;AECn>{;9K`w2*!qLcetzLyg322gAzcM3?iZWVrfDHis@8(Fm&sNgF0
zM|sC;`03oD^GVtioo5aPRmf_b>Ku|bWf}amkvNNU14!(CysGF^KvfC-h;J1--NHZf
zO^C5gl58-OQKox{8go>WQMfPKU_J&5;eD@*Tb~AmzHHkK!N$YMhmc<5Lb|LCO*sZ1
zX+yNsVQas#ejCS=i&Jf#jN};7WgD;wH|gO-Teyy)f5~^dh17cCphIce6dkq>D#I&=
z2K+<<>7hhRJtEjyCaJo{(vTs<(j$+wAzB{k6Tj{T=y37{NDICI0)0M&W4EO#gBQ90Iza|H`Xn8#|W3)
zdS){XTn6+=JblTdmnX}>Ti|)F!J~M_1eb7Pyxp_hgoKN_&l6$x19ySnL7vD)wzcft
zHZq}Wu=i=GDOm78Ee?s<3*Iut&e3U%_Lxe=#vypn+5$B&C+1*Kr1&XajjiJKWIxL^
zQ0ubaAk>G}0O|Ko{!M;}F)NKzVmZs?JCx4G%hi9h593H|6iH}mNZGkUngcX}6Z0Rg
zMe(sy15EY<;bGjhBv!Wgs*QZeSCmrAl|sSHFE2RGG%TbqlmEiHm>ZxsPN5{EuTY%!
z`iZ##hX>3wq_2RqGTvzhjZ(~+^96!~`;fjI!nvEoya%jzPb1edq_5myJ_a#2;5(m0
za%}r@*0*hS%ng_yuqy8d^cB=q%nf)mV5aW`^cB=qv<=`+@>4R^%;U168F`w$9Hm6t
zfTUUv1*qD?0erOIMcIc=MKd7o3%1w}dcB~jRBRlA2dyno3v=R*24CCaXXw|k?x+@b
z!N1z#R}pBFCD^dw!CD*=_h_)v65pi$0p-LNH^ILWW0)y8y3MY^e9i%y-*1wGOv;ct
zPNF~H?{x5tgWnghnar4#4z^`YY`NQNRXiSDU2W<1*y3LON5bJG@F@5c`~fzArQmUJ
zn!m^5BZxN^m}E_Hua=QS`P>uJ~5;tOvV6
zqkP5}{rQ?qkqubZIFQfsC7e?HKaOrB&mfx)KY
zn|v-tsXwPsSQy^}l)Fhv`8JXwiY7^IG(|%<$vDVC$=D0QcHk<;CNNmV*E&~GE`={!
z89T`mXe;{t9a8rr_H3384;-|j9=Y#&xh_^6qX6mG6
zslP+yEI#J|Iki0=)@@)&~-Eih2`9`i_Ch1Ypu9w>4@ax~*+gCb(fc}WpxPGd;+
zik5DtUv(_>nuCeG?JhF)>9W+G;B(k9`PALC*YhmwbvU*i|F3Z>W)UNT4hNfoNiOj5
zi38g%Z1P>TPQN*D7RX#+A*6Fzud^czy$(S<;L=mRcP#;?Tl6+{;S-?EVF~?No?~sf
zen5@9ejMV}fQ0007P8anzR#{~yN0&7uT<^KCCSm&fH!@)<@-;21~ptq@<
zleRessQ2Nb98G^4V>kMj19GyvVk>wBm}G%{)WBFn(hE$9qkFry3<-9Lg@n{zC*#^k
z2MDCxGV;DYKnOP%kkD=TDhru8ZeBXiihSo-a@Nh{0kvjCz^tDSO{tYk4Z+BFilz8c
zKTrdO<`4w!1-6@!XeUB7XW{;hTo(e9XtILt#_JaX&o(hibTjH_R82(knURs_Bh^H8
z@CvD{y+9ATMmV^M+C(`3$!cQ-ANlsE%%w@F;*I9R@DV~Sp{
z8$0HFAg&2#7Dky{pr_Rif#wn57bD$$O5MZ^sg^}~@8Sq-H(bg@?+^4MCq9ALm13A7
zPsYc(nGxKd%;2w8H#U!m>>C81$VuRy$SC*If%iQ*+@I?=j`afxXv+5Tj6`;`F1oEq
zoPfC5L{nLO-gmUGMG!p}{AtSs{Vs54`xrJy7MFw*3#0U*9n(B2@^28qP*#mo{F-}dgeE~;VTkjU*>nI`LWeO=w0nj{s)0E(3*ntF42nCv6*
zLP9ryZCP0->t13R{Ddc;K)&-t7Y_sTwJM2S2y8ELSk*7T^66Gk`p#KC5?}I}Cz`?@
z(|o1yyAKH-55CH)M6zCXR~BWVvA{Lf5^Tp8jnz@(NK{iCv!1iFx=C~sbREc@e=3{A
zR%2Y{F${-xR1rgvCFyA(>xQkhfVv^lP1Aq=1etWnI@!32uJL^)lZ8k-E*})&81O3a
zk)0U73v{0$=(BV_@;b)0AKArKq&nD84E;-}6+RU#Xsf>=aaA2TbzBSzqt}~yBI$Ej
z!rVzVV)Y;x-f@Mgn=nNh3Dk&npsc9y4}-(%&ZFbwQM3br?xI%#Us=n5`#`twWw!*1
zs7p)zJnC8SH?WBwbI%33WNq71*0T9VJ)Xkq9zq3v1#AU+Xf_|*0seD&8g(+APL6{2
if>E8GN0)yaEAS7aJVK+!9r$4Y0000P$
literal 0
HcmV?d00001
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 6840597719..2b9dfcd6f1 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -224,6 +224,14 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let dropInteraction = UIDropInteraction(delegate: self)
self.navigationController?.navigationItem.leftBarButtonItems?.first?.customView?.addInteraction(dropInteraction)
+
+ if(!UserDefaults.standard.bool(forKey: "isInitialPrivacySettingsShowed") || isApplicationUpdated()){
+ redirectToPrivacyViewController()
+
+ //set current app version
+ let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String
+ UserDefaults.standard.set(appVersion, forKey: "CurrentAppVersion")
+ }
registerForTraitChanges([UITraitUserInterfaceStyle.self]) { [weak self] (view: NCCollectionViewCommon, _) in
guard let self else { return }
@@ -313,6 +321,19 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
removeImageCache(metadatas: self.dataSource.getMetadatas())
}
+
+ func isApplicationUpdated() -> Bool{
+ let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? ""
+ let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion")
+ return currentVersion != appVersion
+ }
+
+ func redirectToPrivacyViewController(){
+ let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController
+ newViewController.modalPresentationStyle = .fullScreen
+ self.present(newViewController, animated: true, completion: nil)
+ }
func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
let viewController = presentationController.presentedViewController
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.swift b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
new file mode 100644
index 0000000000..e24216220f
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.swift
@@ -0,0 +1,34 @@
+//
+// AnalysisDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+class AnalysisDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var analysisDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ analysisDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+ }
+
+ override func configure() {
+ super.configure()
+ }
+
+ override func update() {
+ super.update()
+ }
+
+ @objc func switchChanged(mySwitch: UISwitch) {
+ self.rowDescriptor.value = mySwitch.isOn
+ }
+}
+
diff --git a/iOSClient/Settings/AnalysisDataCollectionSwitch.xib b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
new file mode 100644
index 0000000000..45530193f6
--- /dev/null
+++ b/iOSClient/Settings/AnalysisDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/InitialPrivacySettingsViewController.swift b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
new file mode 100644
index 0000000000..2cd30a4242
--- /dev/null
+++ b/iOSClient/Settings/InitialPrivacySettingsViewController.swift
@@ -0,0 +1,170 @@
+//
+// InitialPrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+import UIKit
+
+class InitialPrivacySettingsViewController: UIViewController {
+
+ @IBOutlet weak var dataPrivacyImage: UIImageView!
+ @IBOutlet weak var acceptButton: UIButton!
+ @IBOutlet weak var privacySettingsHelpText: UITextView!
+ @IBOutlet weak var privacySettingsTitle: UILabel!
+ @IBOutlet weak var widthPriavacyHelpView: NSLayoutConstraint!
+ var privacyHelpText = ""
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ privacySettingsTitle.text = NSLocalizedString("_privacy_settings_title_", comment: "")
+ privacyHelpText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ privacySettingsHelpText.text = privacyHelpText
+ dataPrivacyImage.image = UIImage(named: "dataPrivacy")!.image(color: NCBrandColor.shared.brand, size: 60)
+ privacySettingsHelpText.delegate = self
+ privacySettingsHelpText.textColor = .label
+ privacySettingsHelpText.hyperLink(originalText: privacyHelpText,
+ linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): LinkType.privacyPolicy.rawValue,
+ NSLocalizedString("_key_reject_help_", comment: ""): LinkType.reject.rawValue,
+ NSLocalizedString("_key_settings_help_", comment: ""): LinkType.settings.rawValue])
+
+ acceptButton.backgroundColor = NCBrandColor.shared.brand
+ acceptButton.tintColor = UIColor.white
+ acceptButton.layer.cornerRadius = 5
+ acceptButton.layer.borderWidth = 1
+ acceptButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+ acceptButton.setTitle(NSLocalizedString("_accept_button_title_", comment: ""), for: .normal)
+ privacySettingsHelpText.centerText()
+ privacySettingsHelpText.font = UIFont(name: privacySettingsHelpText.font!.fontName, size: 16)
+ self.navigationItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ self.navigationController?.navigationBar.isHidden = true
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ self.navigationController?.navigationBar.isHidden = false
+ }
+
+ override func viewDidLayoutSubviews(){
+ if UIDevice.current.userInterfaceIdiom == .pad {
+ widthPriavacyHelpView.constant = UIScreen.main.bounds.width - 100
+ }
+ }
+
+ @IBAction func onAcceptButtonClicked(_ sender: Any) {
+ requestPermission()
+ }
+
+ //NEWLY ADDED PERMISSIONS FOR iOS 14
+ func requestPermission() {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization { status in
+ switch status {
+ case .authorized:
+ // Tracking authorization dialog was shown
+ // and we are authorized
+ print("Authorized")
+ // Now that we are authorized we can get the IDFA
+ print(ASIdentifierManager.shared().advertisingIdentifier)
+ case .denied:
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ print("Denied")
+ case .notDetermined:
+ // Tracking authorization dialog has not been shown
+ print("Not Determined")
+ case .restricted:
+ print("Restricted")
+ @unknown default:
+ print("Unknown")
+ }
+ }
+ } else {
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ UserDefaults.standard.set(true, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+ self.dismiss(animated: true, completion: nil)
+ }
+}
+// MARK: - UITextViewDelegate
+extension InitialPrivacySettingsViewController: UITextViewDelegate {
+ func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
+ if let linkType = LinkType(rawValue: URL.absoluteString) {
+ // TODO: handle linktype here with switch or similar.
+ switch linkType {
+ case LinkType.privacyPolicy:
+ //let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ let privacyViewController = PrivacyPolicyViewController()
+ self.navigationController?.pushViewController(privacyViewController, animated: true)
+ case LinkType.reject:
+ UserDefaults.standard.set(false, forKey: "isAnalysisDataCollectionSwitchOn")
+ UserDefaults.standard.set(true, forKey: "isInitialPrivacySettingsShowed")
+ self.dismiss(animated: true, completion: nil)
+ case LinkType.settings:
+ let privacySettingsViewController = PrivacySettingsViewController()
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ self.navigationController?.pushViewController(privacySettingsViewController, animated: true)
+ }
+ print("handle link:: \(linkType)")
+ }
+ return false
+ }
+}
+
+public extension UITextView {
+
+ func hyperLink(originalText: String, linkTextsAndTypes: [String: String]) {
+
+ let style = NSMutableParagraphStyle()
+ style.alignment = .left
+
+ let attributedOriginalText = NSMutableAttributedString(string: originalText)
+
+ let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.label, range: fullRange)
+ for linkTextAndType in linkTextsAndTypes {
+ let linkRange = attributedOriginalText.mutableString.range(of: linkTextAndType.key)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: linkTextAndType.value, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: NCBrandColor.shared.brand, range: linkRange)
+ attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
+ }
+
+ self.linkTextAttributes = [NSAttributedString.Key.foregroundColor: NCBrandColor.shared.brand]
+ self.attributedText = attributedOriginalText
+ }
+
+ func centerText() {
+ self.textAlignment = .justified
+ let fittingSize = CGSize(width: 300, height: CGFloat.greatestFiniteMagnitude)
+ let size = sizeThatFits(fittingSize)
+ let topOffset = (bounds.size.height - size.height * zoomScale) / 2
+ let positiveTopOffset = max(1, topOffset)
+ contentOffset.y = -positiveTopOffset
+ }
+}
+
+enum LinkType: String {
+ case reject
+ case privacyPolicy
+ case settings
+}
+
+
diff --git a/iOSClient/Settings/NCSettings.storyboard b/iOSClient/Settings/NCSettings.storyboard
new file mode 100644
index 0000000000..19a320515b
--- /dev/null
+++ b/iOSClient/Settings/NCSettings.storyboard
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/PrivacyPolicyViewController.swift b/iOSClient/Settings/PrivacyPolicyViewController.swift
new file mode 100644
index 0000000000..141c444fca
--- /dev/null
+++ b/iOSClient/Settings/PrivacyPolicyViewController.swift
@@ -0,0 +1,62 @@
+//
+// PrivacyPolicyViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+import WebKit
+
+class PrivacyPolicyViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
+
+ var myWebView = WKWebView()
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.title = NSLocalizedString("_privacy_policy_", comment: "")
+
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ myWebView.uiDelegate = self
+ myWebView.navigationDelegate = self
+ myWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+ self.view.addSubview(myWebView)
+
+ //1. Load web site into my web view
+ let myURL = URL(string: "https://static.magentacloud.de/privacy/datenschutzhinweise_app.htm")
+ let myURLRequest:URLRequest = URLRequest(url: myURL!)
+ NCActivityIndicator.shared.start()
+ myWebView.load(myURLRequest)
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ myWebView = WKWebView(frame: CGRect(x:0, y:0, width: UIScreen.main.bounds.width, height:UIScreen.main.bounds.height))
+ }
+ override func didReceiveMemoryWarning() {
+ super.didReceiveMemoryWarning()
+ // Dispose of any resources that can be recreated.
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ NCActivityIndicator.shared.stop()
+ }
+
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+ if navigationAction.navigationType == .linkActivated {
+ if let url = navigationAction.request.url,
+ UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url)
+ decisionHandler(.cancel)
+ } else {
+ decisionHandler(.allow)
+ }
+ } else {
+ decisionHandler(.allow)
+ }
+ }
+}
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
new file mode 100644
index 0000000000..3e60dd6212
--- /dev/null
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -0,0 +1,133 @@
+//
+// PrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+
+class PrivacySettingsViewController: XLFormViewController{
+
+ @objc public var isShowSettingsButton: Bool = false
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+
+ let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
+ self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
+ isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ changeTheming()
+ }
+
+ @objc func changeTheming() {
+ tableView.backgroundColor = .systemGroupedBackground
+ tableView.separatorColor = .none
+ tableView.separatorColor = .clear
+ tableView.reloadData()
+ initializeForm()
+ }
+
+ //MARK: XLForm
+ func initializeForm() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
+ form.addFormSection(section)
+
+ //custom cell
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
+
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
+ if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
+ }else {
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
+ }
+
+ section.addFormRow(row)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ form.addFormSection(section)
+
+ row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
+ row.cellConfig["backgroundColor"] = UIColor.clear
+
+ if(isShowSettingsButton){
+ section.addFormRow(row)
+ }
+
+ self.form = form
+ }
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "SaveSettingsButton" {
+ print("save settings clicked")
+ //TODO save button state and leave the page
+ self.navigationController?.popViewController(animated: true)
+
+ }
+ if formRow.tag == "AnalysisDataCollectionSwitch"{
+ if (formRow.value! as AnyObject).boolValue {
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
+ if status == .denied {
+ guard let url = URL(string: UIApplication.openSettingsURLString) else {
+ return
+ }
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ })
+ }
+ }
+ UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+ }
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.swift b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
new file mode 100644
index 0000000000..f80aa97e89
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.swift
@@ -0,0 +1,34 @@
+//
+// RequiredDataCollectionSwitch.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+
+class RequiredDataCollectionSwitch: XLFormBaseCell {
+
+ @IBOutlet weak var cellLabel: UILabel!
+ @IBOutlet weak var requiredDataCollectionSwitchControl: UISwitch!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ //requiredDataCollectionSwitchControl.addTarget(self, action: #selector(switchChanged), for: UIControl.Event.valueChanged)
+
+ }
+
+ override func configure() {
+ super.configure()
+
+ requiredDataCollectionSwitchControl.isOn = true
+ requiredDataCollectionSwitchControl.isEnabled = false
+ }
+
+ override func update() {
+ super.update()
+ }
+}
diff --git a/iOSClient/Settings/RequiredDataCollectionSwitch.xib b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
new file mode 100644
index 0000000000..66156c6e06
--- /dev/null
+++ b/iOSClient/Settings/RequiredDataCollectionSwitch.xib
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.swift b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
new file mode 100644
index 0000000000..6205727723
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.swift
@@ -0,0 +1,46 @@
+//
+// SaveSettingsCustomButtonCell.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+
+
+class SaveSettingsCustomButtonCell: XLFormButtonCell {
+
+ @IBOutlet weak var saveSettingsButton: UIButton!
+
+ override func awakeFromNib() {
+ super.awakeFromNib()
+ // Initialization code
+ self.selectionStyle = .none
+ self.separatorInset = UIEdgeInsets(top: 0, left: .greatestFiniteMagnitude, bottom: 0, right: .greatestFiniteMagnitude)
+ saveSettingsButton.setTitle(NSLocalizedString("_save_settings_", comment: ""), for: .normal)
+ saveSettingsButton.addTarget(self, action: #selector(saveButtonClicked), for: .touchUpInside)
+
+ }
+
+ override func configure() {
+ super.configure()
+ saveSettingsButton.backgroundColor = NCBrandColor.shared.brand
+ saveSettingsButton.tintColor = UIColor.white
+ saveSettingsButton.layer.cornerRadius = 5
+ saveSettingsButton.layer.borderWidth = 1
+ saveSettingsButton.layer.borderColor = NCBrandColor.shared.brand.cgColor
+
+ }
+
+ override func update() {
+ super.update()
+
+ }
+
+ @objc func saveButtonClicked(sender: UIButton) {
+ self.rowDescriptor.value = sender
+
+ }
+
+}
diff --git a/iOSClient/Settings/SaveSettingsCustomButtonCell.xib b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
new file mode 100644
index 0000000000..0dd197da31
--- /dev/null
+++ b/iOSClient/Settings/SaveSettingsCustomButtonCell.xib
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From e2941160e3eb1ead980ff941748633e47aa86a61 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Wed, 18 Oct 2023 14:58:46 +0530
Subject: [PATCH 047/177] NMC 1984 - Unit test added for privacy policy view
controller
---
.../PrivacyPolicyTest.swift | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
create mode 100644 Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
diff --git a/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
new file mode 100644
index 0000000000..f6265f48e7
--- /dev/null
+++ b/Tests/NextcloudUnitTests/PrivacyPolicyTest.swift
@@ -0,0 +1,154 @@
+//
+// PrivacyPolicyTest.swift
+// NextcloudTests
+//
+// Created by A200073704 on 27/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+import XLForm
+
+
+ class PrivacyPolicyTest: XCTestCase {
+
+ var viewController: InitialPrivacySettingsViewController?
+ var privacySettingsView = PrivacySettingsViewController()
+
+ override func setUpWithError() throws {
+
+ // To Create an instance of UIStoryboard
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+
+ // To Instantiate UIViewController with Storyboard ID
+ viewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController
+
+ // Outlets are connected
+ let _ = viewController?.view
+
+ // Make the viewDidLoad() execute.
+ viewController?.loadViewIfNeeded()
+
+ }
+
+ override func tearDownWithError() throws {
+ viewController = nil
+ }
+
+ func testPrivacyPolicyViewControllerIsOpen() {
+
+ // Check that the InitialPrivacyPolicyViewController gets opened
+ let storyboard = UIStoryboard(name: "NCSettings", bundle: nil)
+ if let privacyPolicyViewController = storyboard.instantiateViewController(withIdentifier: "privacyPolicyViewController") as? InitialPrivacySettingsViewController {
+ let navigationController = UINavigationController(rootViewController: privacyPolicyViewController)
+
+ privacyPolicyViewController.loadViewIfNeeded()
+
+ XCTAssertTrue(navigationController.topViewController is InitialPrivacySettingsViewController, "Privacy policy view controller should be open")
+ }
+ }
+
+ func testTextViewHasCorrectText() {
+
+ //Check that the text displayed is correct
+ let expectedText = NSLocalizedString("_privacy_help_text_after_login_", comment: "")
+ viewController?.privacySettingsHelpText?.text = expectedText
+
+ let actualText = viewController?.privacySettingsHelpText?.text
+ XCTAssertEqual(actualText, expectedText, "The text view does not have the expected text")
+ }
+
+ func testHasAcceptButton() {
+
+ // Check that view has the accept button
+ let acceptButton = viewController?.acceptButton
+
+ XCTAssertNotNil(acceptButton, "View controller does not have an accept button")
+
+ }
+
+ func testSettingsLinkTypeNavigatesToPrivacySettingsViewController() {
+
+ // Simulate tapping the "Settings" link type
+ let linkType = LinkType.settings
+
+ UserDefaults.standard.set(true, forKey: "showSettingsButton")
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_settings_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation stack
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacySettingsViewController)
+ }
+
+ func testPrivacyPolicyLinkType_NavigatesToPrivacyPolicyViewController() {
+
+ // Simulate tapping the "Privacy Policy" link type
+ let linkType = LinkType.privacyPolicy
+
+ viewController?.privacySettingsHelpText.hyperLink(originalText: viewController?.privacyHelpText ?? "", linkTextsAndTypes: [NSLocalizedString("_key_privacy_help_", comment: ""): linkType.rawValue])
+
+ // Check that the correct view controller was pushed onto the navigation
+ XCTAssertNotNil(viewController?.navigationController?.visibleViewController is PrivacyPolicyViewController)
+ }
+
+ func testCorrectImagePresentOnInitialPrivacySettingsViewController() {
+
+ // Check that the image view has the correct image
+ let expectedImage = UIImage(named: "dataPrivacy")
+ XCTAssertNotNil(expectedImage)
+ }
+
+ func testAcceptButtonHasBackgroundColor() {
+
+ // Check that the accept button has the correct background color
+ let expectedColor = NCBrandColor.shared.brand
+ XCTAssertEqual(viewController?.acceptButton.backgroundColor, expectedColor)
+
+ }
+
+ func testShowSaveSettingsButton() {
+
+ privacySettingsView.isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+
+ XCTAssertTrue(privacySettingsView.isShowSettingsButton)
+
+ }
+
+ func testRequiredDataCollectionSectionExists() {
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // the section with the title "Required Data Collection"
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+
+ // Verify that section was found
+ XCTAssertNotNil(row, "Expected 'Required Data Collection' section to exist in form.")
+ }
+
+ func testAnalysisDataCollectionSection() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // row with tag "AnalysisDataCollectionSwitch"
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+
+ // Assert that the row exists
+ XCTAssertNotNil(row, "Expected row with tag 'AnalysisDataCollectionSwitch' to exist in form.")
+
+ // Verify the switch is off
+ XCTAssertFalse(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn"), "Expected isAnalysisDataCollectionSwitchOn to be false.")
+ }
+
+
+}
From 7eecff1e86dc4e8f131dc279b6ebb2e664262f75 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Mon, 4 Dec 2023 12:01:04 +0530
Subject: [PATCH 048/177] NMC 1984 - privacySettingsViewController file removed
to avoid conflicts
---
.../PrivacySettingsViewController.swift | 133 ------------------
1 file changed, 133 deletions(-)
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
index 3e60dd6212..e69de29bb2 100644
--- a/iOSClient/Settings/PrivacySettingsViewController.swift
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -1,133 +0,0 @@
-//
-// PrivacySettingsViewController.swift
-// Nextcloud
-//
-// Created by A200073704 on 25/04/23.
-// Copyright © 2023 Marino Faggiana. All rights reserved.
-//
-
-import Foundation
-import AppTrackingTransparency
-import AdSupport
-
-class PrivacySettingsViewController: XLFormViewController{
-
- @objc public var isShowSettingsButton: Bool = false
-
- override func viewDidLoad() {
- super.viewDidLoad()
- self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
-
- NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-
- let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
- self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
- isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
- self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
- changeTheming()
- }
-
- @objc func changeTheming() {
- tableView.backgroundColor = .systemGroupedBackground
- tableView.separatorColor = .none
- tableView.separatorColor = .clear
- tableView.reloadData()
- initializeForm()
- }
-
- //MARK: XLForm
- func initializeForm() {
- let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
- form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
-
- var section : XLFormSectionDescriptor
- var row : XLFormRowDescriptor
-
- // Section: Destination Folder
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = " "
- form.addFormSection(section)
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
- form.addFormSection(section)
-
- //custom cell
- section = XLFormSectionDescriptor.formSection(withTitle: "")
- section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
- form.addFormSection(section)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
-
- row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
- row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
- row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
- row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
- row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
- row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
- section.addFormRow(row)
-
- section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
- section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
- form.addFormSection(section)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
-
- row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
- row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
- row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
- row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
- row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
- row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
- if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
- row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
- }else {
- row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
- }
-
- section.addFormRow(row)
-
- XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
-
- section = XLFormSectionDescriptor.formSection(withTitle: "")
- form.addFormSection(section)
-
- row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
- row.cellConfig["backgroundColor"] = UIColor.clear
-
- if(isShowSettingsButton){
- section.addFormRow(row)
- }
-
- self.form = form
- }
-
- override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
- super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
-
- if formRow.tag == "SaveSettingsButton" {
- print("save settings clicked")
- //TODO save button state and leave the page
- self.navigationController?.popViewController(animated: true)
-
- }
- if formRow.tag == "AnalysisDataCollectionSwitch"{
- if (formRow.value! as AnyObject).boolValue {
- if #available(iOS 14, *) {
- ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
- if status == .denied {
- guard let url = URL(string: UIApplication.openSettingsURLString) else {
- return
- }
- if UIApplication.shared.canOpenURL(url) {
- UIApplication.shared.open(url, options: [:])
- }
- }
- })
- }
- }
- UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
- }
- }
-}
From 3cb035ba8581de471e63a15b4b627b5d3037837b Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Tue, 26 Dec 2023 13:29:37 +0530
Subject: [PATCH 049/177] NMC 1984 - Privacy setting view controller added
---
.../PrivacySettingsViewController.swift | 147 ++++++++++++++++++
1 file changed, 147 insertions(+)
diff --git a/iOSClient/Settings/PrivacySettingsViewController.swift b/iOSClient/Settings/PrivacySettingsViewController.swift
index e69de29bb2..b7883ad800 100644
--- a/iOSClient/Settings/PrivacySettingsViewController.swift
+++ b/iOSClient/Settings/PrivacySettingsViewController.swift
@@ -0,0 +1,147 @@
+//
+// PrivacySettingsViewController.swift
+// Nextcloud
+//
+// Created by A200073704 on 25/04/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+import AdSupport
+
+class PrivacySettingsViewController: XLFormViewController{
+
+ @objc public var isShowSettingsButton: Bool = false
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.title = NSLocalizedString("_privacy_settings_title_", comment: "")
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
+
+ let nib = UINib(nibName: "CustomSectionHeader", bundle: nil)
+ self.tableView.register(nib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
+ isShowSettingsButton = UserDefaults.standard.bool(forKey: "showSettingsButton")
+ self.navigationController?.navigationBar.tintColor = NCBrandColor.shared.brand
+ changeTheming()
+ }
+
+ @objc func changeTheming() {
+ tableView.backgroundColor = .systemGroupedBackground
+ tableView.separatorColor = .none
+ tableView.separatorColor = .clear
+ tableView.reloadData()
+ initializeForm()
+ }
+
+
+
+ //MARK: XLForm
+
+ func initializeForm() {
+
+ let form : XLFormDescriptor = XLFormDescriptor() as XLFormDescriptor
+ form.rowNavigationOptions = XLFormRowNavigationOptions.stopDisableRow
+
+ var section : XLFormSectionDescriptor
+ var row : XLFormRowDescriptor
+
+ // Section: Destination Folder
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = " "
+ form.addFormSection(section)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_privacy_settings_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ //custom cell
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ section.footerTitle = NSLocalizedString("_required_data_collection_help_text_", comment: "")
+ form.addFormSection(section)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["RequiredDataCollectionCustomCellType"] = RequiredDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "ButtonDestinationFolder", rowType: "RequiredDataCollectionCustomCellType", title: "")
+ row.cellConfig["requiredDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_required_data_collection_", comment: "")
+ section.addFormRow(row)
+
+ section = XLFormSectionDescriptor.formSection(withTitle: NSLocalizedString("", comment: "").uppercased())
+ section.footerTitle = NSLocalizedString("_analysis_data_acqusition_help_text_", comment: "")
+ form.addFormSection(section)
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["AnalysisDataCollectionCustomCellType"] = AnalysisDataCollectionSwitch.self
+
+
+ row = XLFormRowDescriptor(tag: "AnalysisDataCollectionSwitch", rowType: "AnalysisDataCollectionCustomCellType", title: "")
+ row.cellConfig["analysisDataCollectionSwitchControl.onTintColor"] = NCBrandColor.shared.brand
+ row.cellConfig["cellLabel.textAlignment"] = NSTextAlignment.left.rawValue
+ row.cellConfig["cellLabel.font"] = UIFont.systemFont(ofSize: 15.0)
+ row.cellConfig["cellLabel.textColor"] = UIColor.label //photos
+ row.cellConfig["cellLabel.text"] = NSLocalizedString("_analysis_data_acqusition_", comment: "")
+ if(UserDefaults.standard.bool(forKey: "isAnalysisDataCollectionSwitchOn")){
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 1
+ }else {
+ row.cellConfigAtConfigure["analysisDataCollectionSwitchControl.on"] = 0
+ }
+
+ section.addFormRow(row)
+
+
+ XLFormViewController.cellClassesForRowDescriptorTypes()["SaveSettingsButton"] = SaveSettingsCustomButtonCell.self
+
+ section = XLFormSectionDescriptor.formSection(withTitle: "")
+ form.addFormSection(section)
+
+
+ row = XLFormRowDescriptor(tag: "SaveSettingsButton", rowType: "SaveSettingsButton", title: "")
+ row.cellConfig["backgroundColor"] = UIColor.clear
+
+ if(isShowSettingsButton){
+ section.addFormRow(row)
+ }
+
+
+ self.form = form
+ }
+
+
+ override func formRowDescriptorValueHasChanged(_ formRow: XLFormRowDescriptor!, oldValue: Any!, newValue: Any!) {
+ super.formRowDescriptorValueHasChanged(formRow, oldValue: oldValue, newValue: newValue)
+
+ if formRow.tag == "SaveSettingsButton" {
+ print("save settings clicked")
+ //TODO save button state and leave the page
+ self.navigationController?.popViewController(animated: true)
+
+ }
+ if formRow.tag == "AnalysisDataCollectionSwitch"{
+ if (formRow.value! as AnyObject).boolValue {
+ if #available(iOS 14, *) {
+ ATTrackingManager.requestTrackingAuthorization(completionHandler: { (status) in
+ if status == .denied {
+ guard let url = URL(string: UIApplication.openSettingsURLString) else {
+ return
+ }
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ })
+ }
+ }
+ UserDefaults.standard.set((formRow.value! as AnyObject).boolValue, forKey: "isAnalysisDataCollectionSwitchOn")
+ }
+
+ }
+
+}
From 028bd37e5bc2a77704db383a293334c0e4aee6fd Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Wed, 9 Apr 2025 14:10:39 +0530
Subject: [PATCH 050/177] NMC 1984 - Privacy policy customisation changes
added
---
Nextcloud.xcodeproj/project.pbxproj | 47 ++
...mon+CollectionViewDelegateFlowLayout.swift | 43 ++
.../NCCollectionViewCommon.swift | 652 ++++++++++++++++--
3 files changed, 677 insertions(+), 65 deletions(-)
diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj
index d3dc9dee77..50fd2da1ff 100644
--- a/Nextcloud.xcodeproj/project.pbxproj
+++ b/Nextcloud.xcodeproj/project.pbxproj
@@ -91,6 +91,19 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
+ AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
+ B54315362DA64EB100981E7E /* PrivacyPolicyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */; };
+ B54315412DA669C700981E7E /* InitialPrivacySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */; };
+ B54315422DA669C700981E7E /* RequiredDataCollectionSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */; };
+ B54315432DA669C700981E7E /* PrivacySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */; };
+ B54315442DA669C700981E7E /* AnalysisDataCollectionSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */; };
+ B54315452DA669C700981E7E /* PrivacyPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */; };
+ B54315462DA669C700981E7E /* SaveSettingsCustomButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */; };
+ B54315472DA669C700981E7E /* AnalysisDataCollectionSwitch.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */; };
+ B54315482DA669C700981E7E /* NCSettings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B543153A2DA669C700981E7E /* NCSettings.storyboard */; };
+ B54315492DA669C700981E7E /* SaveSettingsCustomButtonCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */; };
+ B543154A2DA669C700981E7E /* RequiredDataCollectionSwitch.xib in Resources */ = {isa = PBXBuildFile; fileRef = B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */; };
+ C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
@@ -1368,6 +1381,18 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; };
+ AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; };
+ B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyTest.swift; sourceTree = ""; };
+ B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalysisDataCollectionSwitch.swift; sourceTree = ""; };
+ B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AnalysisDataCollectionSwitch.xib; sourceTree = ""; };
+ B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialPrivacySettingsViewController.swift; sourceTree = ""; };
+ B543153A2DA669C700981E7E /* NCSettings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCSettings.storyboard; sourceTree = ""; };
+ B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyViewController.swift; sourceTree = ""; };
+ B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacySettingsViewController.swift; sourceTree = ""; };
+ B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequiredDataCollectionSwitch.swift; sourceTree = ""; };
+ B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RequiredDataCollectionSwitch.xib; sourceTree = ""; };
+ B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveSettingsCustomButtonCell.swift; sourceTree = ""; };
+ B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SaveSettingsCustomButtonCell.xib; sourceTree = ""; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; };
@@ -2173,6 +2198,8 @@
isa = PBXGroup;
children = (
AA52EB452D42AC5A0089C348 /* Placeholder.swift */,
+ B54315352DA64EB100981E7E /* PrivacyPolicyTest.swift */,
+ AF8ED1FB2757821000B8DBC4 /* NextcloudUnitTests.swift */,
);
path = NextcloudUnitTests;
sourceTree = "";
@@ -2649,6 +2676,16 @@
F76882042C0DD1E7001CF441 /* Settings */ = {
isa = PBXGroup;
children = (
+ B54315372DA669C700981E7E /* AnalysisDataCollectionSwitch.swift */,
+ B54315382DA669C700981E7E /* AnalysisDataCollectionSwitch.xib */,
+ B54315392DA669C700981E7E /* InitialPrivacySettingsViewController.swift */,
+ B543153A2DA669C700981E7E /* NCSettings.storyboard */,
+ B543153B2DA669C700981E7E /* PrivacyPolicyViewController.swift */,
+ B543153C2DA669C700981E7E /* PrivacySettingsViewController.swift */,
+ B543153D2DA669C700981E7E /* RequiredDataCollectionSwitch.swift */,
+ B543153E2DA669C700981E7E /* RequiredDataCollectionSwitch.xib */,
+ B543153F2DA669C700981E7E /* SaveSettingsCustomButtonCell.swift */,
+ B54315402DA669C700981E7E /* SaveSettingsCustomButtonCell.xib */,
F768820B2C0DD1E7001CF441 /* Settings */,
F76882162C0DD1E7001CF441 /* AutoUpload */,
F389C9F32CEE381E00049762 /* SelectAlbum */,
@@ -4076,6 +4113,10 @@
3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */,
AF93471C27E2361E002537EE /* NCShareHeader.xib in Resources */,
F75D901F2D2BE12E003E740B /* NCRecommendationsCell.xib in Resources */,
+ B54315472DA669C700981E7E /* AnalysisDataCollectionSwitch.xib in Resources */,
+ B54315482DA669C700981E7E /* NCSettings.storyboard in Resources */,
+ B54315492DA669C700981E7E /* SaveSettingsCustomButtonCell.xib in Resources */,
+ B543154A2DA669C700981E7E /* RequiredDataCollectionSwitch.xib in Resources */,
F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */,
F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */,
F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */,
@@ -4723,6 +4764,12 @@
F72408332B8A27C900F128E2 /* NCMedia+Command.swift in Sources */,
F755CB402B8CB13C00CE27E9 /* NCMediaLayout.swift in Sources */,
F73EF7B72B0224AB0087E6E9 /* NCManageDatabase+ExternalSites.swift in Sources */,
+ B54315412DA669C700981E7E /* InitialPrivacySettingsViewController.swift in Sources */,
+ B54315422DA669C700981E7E /* RequiredDataCollectionSwitch.swift in Sources */,
+ B54315432DA669C700981E7E /* PrivacySettingsViewController.swift in Sources */,
+ B54315442DA669C700981E7E /* AnalysisDataCollectionSwitch.swift in Sources */,
+ B54315452DA669C700981E7E /* PrivacyPolicyViewController.swift in Sources */,
+ B54315462DA669C700981E7E /* SaveSettingsCustomButtonCell.swift in Sources */,
AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */,
AFA2AC8527849604008E1EA7 /* NCActivityCommentView.swift in Sources */,
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
index 06795639bc..42bbbd6038 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegateFlowLayout.swift
@@ -13,4 +13,47 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return sizeForFooterInSection(section: section)
}
+
+ func getHeaderHeight() -> CGFloat {
+
+ var size: CGFloat = 0
+ // transfer in progress
+ if headerMenuTransferView,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(NCNetworking.shared.transferInForegorund?.ocId),
+ metadata.isTransferInForeground {
+ if !isSearchingMode {
+ size += NCGlobal.shared.heightHeaderTransfer
+ }
+ } else {
+ NCNetworking.shared.transferInForegorund = nil
+ }
+
+ if headerMenuButtonsView {
+ size += NCGlobal.shared.heightButtonsView
+ }
+
+ return size
+ }
+
+ func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
+
+ var headerRichWorkspace: CGFloat = 0
+
+ if let richWorkspaceText = richWorkspaceText, showDescription {
+ let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces)
+ if !trimmed.isEmpty && !isSearchingMode {
+ headerRichWorkspace = UIScreen.main.bounds.size.height / 6
+ }
+ }
+
+ if isSearchingMode || layoutForView?.layout == NCGlobal.shared.layoutGrid || dataSource.numberOfSections() > 1 {
+ if section == 0 {
+ return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
+ } else {
+ return (0, 0, NCGlobal.shared.heightSection)
+ }
+ } else {
+ return (getHeaderHeight(), headerRichWorkspace, 0)
+ }
+ }
}
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 2b9dfcd6f1..2a8cb39d2a 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -8,7 +8,7 @@ import RealmSwift
import NextcloudKit
import EasyTipView
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionFirstHeaderDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, NCTransferDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
@@ -56,10 +56,17 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var titleCurrentFolder = ""
var titlePreviusFolder: String?
var enableSearchBar: Bool = false
+ let maxImageGrid: CGFloat = 7
+ var headerMenu: NCSectionHeaderMenu?
+ var headerMenuTransferView = false
+ var headerMenuButtonsView: Bool = true
var headerRichWorkspaceDisable: Bool = false
+
+ var groupByField = "name"
var emptyImageName: String?
var emptyImageColors: [UIColor]?
+ var emptyImage: UIImage?
var emptyTitle: String = ""
var emptyDescription: String = ""
@@ -78,6 +85,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var numberOfColumns: Int = 0
var lastNumberOfColumns: Int = 0
+ var isTransitioning: Bool = false
+ var selectableDataSource: [RealmSwiftObject] { dataSource.getMetadataSourceForAllSections() }
+ var pushed: Bool = false
+ var emptyDataSet: NCEmptyDataSet?
+
let heightHeaderRecommendations: CGFloat = 160
let heightHeaderSection: CGFloat = 30
@@ -162,8 +174,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
view.backgroundColor = .systemBackground
collectionView.backgroundColor = .systemBackground
- refreshControl.tintColor = .clear
-
+ refreshControl.tintColor = .gray
+
+ listLayout = NCListLayout()
+ gridLayout = NCGridLayout()
+
if enableSearchBar {
searchController = UISearchController(searchResultsController: nil)
searchController?.searchResultsUpdater = self
@@ -172,7 +187,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
searchController?.searchBar.delegate = self
searchController?.searchBar.autocapitalizationType = .none
navigationItem.searchController = searchController
- navigationItem.hidesSearchBarWhenScrolling = true
+ navigationItem.hidesSearchBarWhenScrolling = false
+ navigationItem.backBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_back_", comment: ""), style: .plain, target: nil, action: nil)
}
// Cell
@@ -182,12 +198,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView.register(UINib(nibName: "NCTransferCell", bundle: nil), forCellWithReuseIdentifier: "transferCell")
// Header
- collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeader")
- collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeader")
+ collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeader")
- collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionHeader")
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
- collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData")
// Footer
collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
@@ -207,6 +219,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.mainNavigationController?.resetPlusButtonAlpha()
}
}
+
+ // Empty
+ emptyDataSet = NCEmptyDataSet(view: collectionView, offset: getHeaderHeight(), delegate: self)
let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:)))
longPressedGesture.minimumPressDuration = 0.5
@@ -256,6 +271,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
navigationController?.navigationBar.topItem?.title = titlePreviusFolder
}
navigationItem.title = titleCurrentFolder
+ navigationController?.setNavigationBarAppearance()
+ navigationController?.navigationBar.prefersLargeTitles = true
+ navigationController?.setNavigationBarHidden(false, animated: true)
+
+ appDelegate.activeViewController = self
if tabBarSelect == nil {
tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, viewController: self, delegate: self)
@@ -263,12 +283,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
isEditMode = false
- Task {
- await (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems()
- await (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
- }
+ /// Magentacloud branding changes hide user account button on left navigation bar
+// setNavigationLeftItems()
+ setNavigationRightItems()
layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
+ gridLayout.column = CGFloat(layoutForView?.columnGrid ?? 3)
if isLayoutList {
collectionView?.collectionViewLayout = listLayout
self.layoutType = global.layoutList
@@ -295,6 +315,30 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(closeRichWorkspaceWebView), name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(changeStatusFolderE2EE(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeStatusFolderE2EE), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(changeLayout(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeLayout), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(copyMoveFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterCopyMoveFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterRenameFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(createFolder(_:)), name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(favoriteFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadStartFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(downloadCancelFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
+
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadStartFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadStartFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadedLivePhoto(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(uploadCancelFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(updateShare(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: global.notificationCenterProgressTask), object: nil)
+
+ // FIXME: iPAD PDF landscape mode iOS 16
+ DispatchQueue.main.async {
+ self.collectionView?.collectionViewLayout.invalidateLayout()
+ }
}
override func viewWillDisappear(_ animated: Bool) {
@@ -302,7 +346,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.networking.cancelUnifiedSearchFiles()
dismissTip()
-
+ pushed = false
+ toggleSelect(isOn: false)
// Cancel Queue & Retrieves Properties
self.networking.downloadThumbnailQueue.cancelAll()
self.networking.unifiedSearchQueue.cancelAll()
@@ -318,17 +363,38 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeStatusFolderE2EE), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterChangeLayout), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCopyFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterRenameFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadStartFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil)
+
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
removeImageCache(metadatas: self.dataSource.getMetadatas())
}
- func isApplicationUpdated() -> Bool{
+ func isApplicationUpdated() -> Bool {
let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? ""
let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion")
return currentVersion != appVersion
}
- func redirectToPrivacyViewController(){
+ func redirectToPrivacyViewController() {
let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController
newViewController.modalPresentationStyle = .fullScreen
@@ -604,8 +670,352 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.present(alertController, animated: true)
}
+ self.collectionView.collectionViewLayout.invalidateLayout()
+
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
+ }
+
+ @objc func reloadDataSource(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as? NSDictionary {
+ if let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ if let clearDataSource = userInfo["clearDataSource"] as? Bool, clearDataSource {
+ self.dataSource.removeAll()
+ }
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func getServerData(_ notification: NSNotification) {
+ if let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String {
+ if serverUrl != self.serverUrl {
+ return
+ }
+ }
+
+ getServerData()
+ }
+
+ @objc func reloadHeader(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ account == session.account
+ else { return }
+
+ self.collectionView.reloadData()
+ }
+
+ @objc func changeStatusFolderE2EE(_ notification: NSNotification) {
+ reloadDataSource()
+ }
+
+ @objc func closeRichWorkspaceWebView() {
+ reloadDataSource()
}
+ @objc func deleteFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ } else {
+ NCContentPresenter().showError(error: error)
+ }
+
+ reloadDataSource()
+ }
+
+ @objc func copyMoveFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ reloadDataSource()
+ }
+ }
+
+ @objc func renameFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let error = userInfo["error"] as? NKError,
+ account == session.account
+ else { return }
+
+ if error == .success {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ }
+
+ if serverUrl == self.serverUrl {
+ if error != .success {
+ NCContentPresenter().showError(error: error)
+ }
+ reloadDataSource()
+ } else {
+ collectionView.reloadData()
+ }
+ }
+
+ @objc func createFolder(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let account = userInfo["account"] as? String,
+ account == session.account,
+ let withPush = userInfo["withPush"] as? Bool,
+ let metadata = database.getMetadataFromOcId(ocId)
+ else { return }
+
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if metadata.serverUrl + "/" + metadata.fileName == self.serverUrl {
+ reloadDataSource()
+ } else if withPush, metadata.serverUrl == self.serverUrl {
+ reloadDataSource()
+ if let sceneIdentifier = userInfo["sceneIdentifier"] as? String {
+ if sceneIdentifier == controller?.sceneIdentifier {
+ pushMetadata(metadata)
+ }
+ } else {
+ pushMetadata(metadata)
+ }
+ }
+ }
+
+ @objc func favoriteFile(_ notification: NSNotification) {
+ if isSearchingMode {
+ return networkSearch()
+ }
+
+ if self is NCFavorite {
+ return reloadDataSource()
+ }
+
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ serverUrl == self.serverUrl
+ else { return }
+
+ reloadDataSource()
+ }
+
+ @objc func downloadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func downloadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadStartFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let ocId = userInfo["ocId"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String,
+ !isSearchingMode,
+ let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)
+ else { return }
+
+ // Header view trasfer
+ if metadata.isTransferInForeground {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: 0)
+ DispatchQueue.main.async { self.collectionView?.reloadData() }
+ }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadedLivePhoto(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func uploadCancelFile(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let account = userInfo["account"] as? String
+ else { return }
+
+ if account == self.session.account, serverUrl == self.serverUrl {
+ reloadDataSource()
+ } else {
+ collectionView?.reloadData()
+ }
+ }
+
+ @objc func updateShare(_ notification: NSNotification) {
+ if isSearchingMode {
+ networkSearch()
+ } else {
+ self.dataSource.removeAll()
+ getServerData()
+ }
+ }
+
+ @objc func triggerProgressTask(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let progressNumber = userInfo["progress"] as? NSNumber,
+ let totalBytes = userInfo["totalBytes"] as? Int64,
+ let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
+ let ocId = userInfo["ocId"] as? String,
+ let ocIdTransfer = userInfo["ocIdTransfer"] as? String,
+ let session = userInfo["session"] as? String
+ else { return }
+
+ let chunk: Int = userInfo["chunk"] as? Int ?? 0
+ let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
+
+ let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected))
+
+ // HEADER
+// if self.headerMenuTransferView, transfer.session.contains("upload") {
+// self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
+// self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
+// }
+
+ DispatchQueue.main.async {
+ if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
+ if NCNetworking.shared.transferInForegorund?.ocId == ocId {
+ NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
+ } else {
+ NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: progressNumber.floatValue)
+ self.collectionView.reloadData()
+ }
+ self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue
+ } else {
+ guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath,
+ let cell = self.collectionView?.cellForItem(at: indexPath),
+ let cell = cell as? NCCellProtocol else { return }
+ if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
+ cell.fileProgressView?.isHidden = true
+ cell.fileProgressView?.progress = .zero
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore)
+ if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+ cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+ } else {
+ cell.fileInfoLabel?.text = ""
+ cell.fileSubinfoLabel?.text = ""
+ }
+ } else {
+ cell.fileProgressView?.isHidden = false
+ cell.fileProgressView?.progress = progressNumber.floatValue
+ cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop)
+ let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
+ if status == NCGlobal.shared.metadataStatusDownloading {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else if status == NCGlobal.shared.metadataStatusUploading {
+ if totalBytes > 0 {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ " + self.utilityFileSystem.transformedSize(totalBytes)
+ } else {
+ cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
+ cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ …"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // MARK: - Layout
+
+ func setNavigationLeftItems() {
+ navigationItem.title = titleCurrentFolder
+ }
+
func getNavigationTitle() -> String {
let tblAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
if let tblAccount,
@@ -647,6 +1057,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
self.navigationItem.title = self.titleCurrentFolder
}
+ // MARK: - Empty
+
+ func emptyDataSetView(_ view: NCEmptyView) {
+
+ self.emptyDataSet?.setOffset(getHeaderHeight())
+ if isSearchingMode {
+ view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ if self.dataSourceTask?.state == .running {
+ view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
+ } else {
+ view.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "")
+ }
+ view.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "")
+ } else if self.dataSourceTask?.state == .running {
+ view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width)
+ view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "")
+ view.emptyDescription.text = ""
+ } else {
+ if serverUrl.isEmpty {
+ view.emptyImage.image = emptyImage
+ view.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "")
+ view.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "")
+ } else {
+ view.emptyImage.image = UIImage(named: "folder_nmcloud")
+ view.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "")
+ view.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "")
+ }
+ }
+ }
+
// MARK: - SEARCH
func searchController(enabled: Bool) {
@@ -699,23 +1139,34 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - TAP EVENT
- func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
- tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
- func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
- tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) {
+ if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
-
NCDownloadAction.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
+ TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""])
+ appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue))
+ NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
}
- func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ func tapMoreGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
+ if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
- toggleMenu(metadata: metadata, image: image, sender: sender)
+// toggleMenu(metadata: metadata, image: image)
+ if namedButtonMore == NCGlobal.shared.buttonMoreMore || namedButtonMore == NCGlobal.shared.buttonMoreLock {
+ toggleMenu(metadata: metadata, image: image)
+ } else if namedButtonMore == NCGlobal.shared.buttonMoreStop {
+ Task {
+ await cancelSession(metadata: metadata)
+ }
+ }
}
func tapRichWorkspace(_ sender: Any) {
@@ -743,16 +1194,50 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
didSelectMetadata(metadata, withOcIds: false)
}
- func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonSwitch(_ sender: Any) {
+ guard !isTransitioning else { return }
+ isTransitioning = true
- func longPressGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return }
- func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ if layoutForView.layout == NCGlobal.shared.layoutGrid {
+ layoutForView.layout = NCGlobal.shared.layoutList
+ } else {
+ layoutForView.layout = NCGlobal.shared.layoutGrid
+ }
+ self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
+ self.collectionView.reloadData()
+ self.collectionView.collectionViewLayout.invalidateLayout()
+ self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false }
+ }
- func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonOrder(_ sender: Any) {
+
+// if let titleButtonHeader = NCKeychain().getTitleButtonHeader(account: session.account), !titleButtonHeader.isEmpty {
+// layoutForView?.titleButtonHeader = titleButtonHeader
+// }
+// NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView!)
+
+ let sortMenu = NCSortMenu()
+ sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
+ }
- func longPressMoreGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { }
+ func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressMoreGridItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func tapButtonTransfer(_ sender: Any) { }
+
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
+
@objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) {
openMenuItems(with: nil, gestureRecognizer: gestureRecognizer)
}
@@ -807,11 +1292,27 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - DataSource
- @MainActor
- func reloadDataSource() async {
- if !isSearchingMode {
- Task.detached {
- if await self.isRecommendationActived() {
+ @objc func reloadDataSource() async {
+
+ // get auto upload folder
+ autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
+ autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account)
+
+ // get layout for view
+ layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
+ // set GroupField for Grid
+ if !isSearchingMode && layoutForView?.layout == NCGlobal.shared.layoutGrid {
+ groupByField = "classFile"
+ } else {
+ groupByField = "name"
+ }
+
+ if isSearchingMode {
+ isDirectoryEncrypted = false
+ } else {
+ isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
+ if isRecommendationActived {
+ Task.detached {
await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
}
}
@@ -823,7 +1324,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
animations: { self.collectionView.reloadData() },
completion: nil)
- await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ self.refreshControl.endRefreshing()
+ self.collectionView.reloadData()
+ self.setNavigationRightItems()
+ }
}
func getServerData(forced: Bool = false) async { }
@@ -955,39 +1460,56 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
- // MARK: - Header size
-
- func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
- heightHeaderRecommendations: CGFloat,
- heightHeaderSection: CGFloat) {
- var heightHeaderRichWorkspace: CGFloat = 0
- var heightHeaderRecommendations: CGFloat = 0
- var heightHeaderSection: CGFloat = 0
+ func pushViewController(viewController: UIViewController) {
+ if pushed { return }
- if showDescription,
- !isSearchingMode,
- let richWorkspaceText = self.richWorkspaceText,
- !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
- heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
- }
-
- if isRecommendationActived,
- !isSearchingMode,
- NCPreferences().showRecommendedFiles,
- !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
- heightHeaderRecommendations = self.heightHeaderRecommendations
- heightHeaderSection = self.heightHeaderSection
- }
+ pushed = true
+ navigationController?.pushViewController(viewController, animated: true)
+ }
+
+ // MARK: - Header size
- if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
- if section == 0 {
- return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
- } else {
- return (0, 0, self.heightHeaderSection)
- }
- } else {
- return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
+// heightHeaderRecommendations: CGFloat,
+// heightHeaderSection: CGFloat) {
+// var heightHeaderRichWorkspace: CGFloat = 0
+// var heightHeaderRecommendations: CGFloat = 0
+// var heightHeaderSection: CGFloat = 0
+//
+// if showDescription,
+// !isSearchingMode,
+// let richWorkspaceText = self.richWorkspaceText,
+// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
+// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
+// }
+//
+// if isRecommendationActived,
+// !isSearchingMode,
+// NCKeychain().showRecommendedFiles,
+// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
+// heightHeaderRecommendations = self.heightHeaderRecommendations
+// heightHeaderSection = self.heightHeaderSection
+// }
+//
+// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
+// if section == 0 {
+// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
+// } else {
+// return (0, 0, self.heightHeaderSection)
+// }
+// } else {
+// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+// }
+// }
+
+ func isHeaderMenuTransferViewEnabled() -> [tableMetadata]? {
+ if headerMenuTransferView,
+ NCNetworking.shared.isOnline,
+ let results = database.getResultsMetadatas(predicate: NSPredicate(format: "status IN %@", [global.metadataStatusWaitUpload, global.metadataStatusUploading])),
+ !results.isEmpty {
+ return Array(results)
}
+ return nil
}
func sizeForHeaderInSection(section: Int) -> CGSize {
@@ -996,7 +1518,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let isIphone = UIDevice.current.userInterfaceIdiom == .phone
if self.dataSource.isEmpty() {
- height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset)
+ height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil)
} else if isEditMode || (isLandscape && isIphone) {
return CGSize.zero
} else {
From daca9b9d97f2448072588de61798d7e2ccd91b94 Mon Sep 17 00:00:00 2001
From: harshada-15-tsys
Date: Tue, 30 Sep 2025 22:00:01 +0530
Subject: [PATCH 051/177] NMC 1984 - Privacy policy customisation added
---
.../NCCollectionViewCommon.swift | 758 +++++++-----------
1 file changed, 268 insertions(+), 490 deletions(-)
diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
index 2a8cb39d2a..f3773d3ecf 100644
--- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
+++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
@@ -4,11 +4,13 @@
import UIKit
import SwiftUI
+import Realm
import RealmSwift
import NextcloudKit
import EasyTipView
+import MoEngageInApps
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, NCTransferDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate {
@IBOutlet weak var collectionView: UICollectionView!
@@ -18,7 +20,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let utilityFileSystem = NCUtilityFileSystem()
let imageCache = NCImageCache.shared
var dataSource = NCCollectionViewDataSource()
- let networking = NCNetworking.shared
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
var pinchGesture: UIPinchGestureRecognizer = UIPinchGestureRecognizer()
@@ -29,7 +30,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var backgroundImageView = UIImageView()
var serverUrl: String = ""
var isEditMode = false
- var isDirectoryE2EE = false
+ var isDirectoryEncrypted = false
var fileSelect: [String] = []
var metadataFolder: tableMetadata?
var richWorkspaceText: String?
@@ -38,7 +39,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var isSearchingMode: Bool = false
var networkSearchInProgress: Bool = false
var layoutForView: NCDBLayoutForView?
- var searchDataSourceTask: URLSessionTask?
+ var dataSourceTask: URLSessionTask?
var providers: [NKSearchProvider]?
var searchResults: [NKSearchResult]?
var listLayout = NCListLayout()
@@ -49,18 +50,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
var tabBarSelect: NCCollectionViewCommonSelectTabBar?
var attributesZoomIn: UIMenuElement.Attributes = []
var attributesZoomOut: UIMenuElement.Attributes = []
- var tipViewAccounts: EasyTipView?
+ let maxImageGrid: CGFloat = 7
+// var headerMenu: NCSectionFirstHeader?
+ var tipViewAccounts: EasyTipView?
+ var tipViewAutoUpload: EasyTipView?
+ var headerMenu: NCSectionHeaderMenu?
+ var headerMenuTransferView = false
+ var headerMenuButtonsView: Bool = true
+ var headerRichWorkspaceDisable: Bool = false
+
// DECLARE
var layoutKey = ""
var titleCurrentFolder = ""
var titlePreviusFolder: String?
var enableSearchBar: Bool = false
- let maxImageGrid: CGFloat = 7
- var headerMenu: NCSectionHeaderMenu?
- var headerMenuTransferView = false
- var headerMenuButtonsView: Bool = true
- var headerRichWorkspaceDisable: Bool = false
var groupByField = "name"
@@ -93,7 +97,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let heightHeaderRecommendations: CGFloat = 160
let heightHeaderSection: CGFloat = 30
- @MainActor
var session: NCSession.Session {
NCSession.shared.getSession(controller: tabBarController)
}
@@ -111,29 +114,30 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
var showDescription: Bool {
- !headerRichWorkspaceDisable && NCPreferences().showDescription
+ !headerRichWorkspaceDisable && NCKeychain().showDescription
}
var isRecommendationActived: Bool {
- let capabilities = NCNetworking.shared.capabilities[session.account] ?? NKCapabilities.Capabilities()
- return self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && capabilities.recommendations
+ self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) &&
+ NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations
}
var infoLabelsSeparator: String {
layoutForView?.layout == global.layoutList ? " - " : ""
}
- @MainActor
var controller: NCMainTabBarController? {
self.tabBarController as? NCMainTabBarController
}
- var mainNavigationController: NCMainNavigationController? {
- self.navigationController as? NCMainNavigationController
+ var defaultPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, NCGlobal.shared.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
}
- var sceneIdentifier: String {
- (self.tabBarController as? NCMainTabBarController)?.sceneIdentifier ?? ""
+ var personalFilesOnlyPredicate: NSPredicate {
+ let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue)
+ return predicate
}
var isNumberOfItemsInAllSectionsNull: Bool {
@@ -156,18 +160,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
return pinchGesture.state == .began || pinchGesture.state == .changed
}
- func isRecommendationActived() async -> Bool {
- let capabilities = await NKCapabilities.shared.getCapabilities(for: session.account)
- return self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && capabilities.recommendations
- }
-
- internal let debouncer = NCDebouncer(delay: 1)
-
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
+ tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, delegate: self)
self.navigationController?.presentationController?.delegate = self
collectionView.alwaysBounceVertical = true
collectionView.accessibilityIdentifier = "NCCollectionViewCommon"
@@ -207,16 +205,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView.refreshControl = refreshControl
refreshControl.action(for: .valueChanged) { _ in
- Task { @MainActor in
- // Perform async server forced
- await self.getServerData(forced: true)
-
- // Stop the refresh control after data is loaded
- self.refreshControl.endRefreshing()
-
- // Wait 1.5 seconds before resetting the button alpha
- try? await Task.sleep(nanoseconds: 1_500_000_000)
- self.mainNavigationController?.resetPlusButtonAlpha()
+ self.dataSource.removeAll()
+ self.getServerData()
+ if self.isRecommendationActived {
+ Task.detached {
+ await NCNetworking.shared.createRecommendations(session: self.session)
+ }
+ }
+ self.refreshControl.endRefreshing()
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
+ self.resetPlusButtonAlpha()
}
}
@@ -247,17 +245,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String
UserDefaults.standard.set(appVersion, forKey: "CurrentAppVersion")
}
-
- registerForTraitChanges([UITraitUserInterfaceStyle.self]) { [weak self] (view: NCCollectionViewCommon, _) in
- guard let self else { return }
-
- self.sectionFirstHeader?.setRichWorkspaceColor(style: view.traitCollection.userInterfaceStyle)
- }
-
- NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: self.global.notificationCenterChangeTheming), object: nil, queue: .main) { [weak self] _ in
- guard let self else { return }
- self.collectionView.reloadData()
- }
+
+ NotificationCenter.default.addObserver(self, selector: #selector(changeTheming(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeTheming), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadDataSource), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(getServerData(_:)), name: NSNotification.Name(rawValue: global.notificationCenterGetServerData), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(reloadHeader(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadHeader), object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(updateIcons), name: NSNotification.Name(rawValue: global.notificationCenterUpdateIcons), object: nil)
DispatchQueue.main.async {
self.collectionView?.collectionViewLayout.invalidateLayout()
@@ -267,6 +260,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
+ AnalyticsHelper.shared.displayInAppNotification()
+
if titlePreviusFolder != nil {
navigationController?.navigationBar.topItem?.title = titlePreviusFolder
}
@@ -276,11 +271,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
navigationController?.setNavigationBarHidden(false, animated: true)
appDelegate.activeViewController = self
+ appDelegate.account = session.account
+ appDelegate.urlBase = session.urlBase
+ appDelegate.userId = session.userId
+ appDelegate.user = session.user
- if tabBarSelect == nil {
- tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, viewController: self, delegate: self)
- }
-
+ NCKeychain().setAccountName(account: session.account)
isEditMode = false
/// Magentacloud branding changes hide user account button on left navigation bar
@@ -309,9 +305,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
- Task {
- await NCNetworking.shared.transferDispatcher.addDelegate(self)
- }
+ NCNetworking.shared.transferDelegate = self
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(closeRichWorkspaceWebView), name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
@@ -344,22 +338,20 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
- self.networking.cancelUnifiedSearchFiles()
+ NCNetworking.shared.cancelUnifiedSearchFiles()
dismissTip()
pushed = false
toggleSelect(isOn: false)
// Cancel Queue & Retrieves Properties
- self.networking.downloadThumbnailQueue.cancelAll()
- self.networking.unifiedSearchQueue.cancelAll()
- searchDataSourceTask?.cancel()
+ NCNetworking.shared.downloadThumbnailQueue.cancelAll()
+ NCNetworking.shared.unifiedSearchQueue.cancelAll()
+ dataSourceTask?.cancel()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
- Task {
- await NCNetworking.shared.transferDispatcher.removeDelegate(self)
- }
+ NCNetworking.shared.transferDelegate = nil
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCloseRichWorkspaceWebView), object: nil)
@@ -374,6 +366,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterCreateFolder), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterFavoriteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil)
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadStartFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadedFile), object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterDownloadCancelFile), object: nil)
@@ -385,7 +382,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil)
- removeImageCache(metadatas: self.dataSource.getMetadatas())
+ dataSource.removeImageCache()
}
func isApplicationUpdated() -> Bool {
@@ -426,250 +423,68 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
return true
}
- // MARK: - Transfer Delegate
-
- func transferProgressDidUpdate(progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String) { }
+ override func viewWillLayoutSubviews() {
+ super.viewWillLayoutSubviews()
- func transferChange(status: String, metadatasError: [tableMetadata: NKError]) {
- switch status {
- // DELETE
- case self.global.networkingStatusDelete:
- let errorForThisServer = metadatasError.first { entry in
- let (key, value) = entry
- return key.serverUrl == self.serverUrl && value != .success
- }?.value
-
- let needLoadDataSource = metadatasError.contains { entry in
- let (key, value) = entry
- return key.serverUrl == self.serverUrl && value == .success
- }
-
- if let error = errorForThisServer {
- NCContentPresenter().showError(error: error)
- }
-
- if self.isSearchingMode {
- self.networkSearch()
- } else if needLoadDataSource {
- Task {
- await self.reloadDataSource()
- }
- } else {
- Task.detached {
- if await self.isRecommendationActived() {
- await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
- }
- }
- }
- default:
- break
- }
- }
-
- func transferChange(status: String, metadata: tableMetadata, error: NKError) {
- guard session.account == metadata.account else { return }
-
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- DispatchQueue.main.async {
- switch status {
- // UPLOADED, UPLOADED LIVEPHOTO
- case self.global.networkingStatusUploaded, self.global.networkingStatusUploadedLivePhoto:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- // DOWNLOAD
- case self.global.networkingStatusDownloading:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- case self.global.networkingStatusDownloaded:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- case self.global.networkingStatusDownloadCancel:
- Task {
- if metadata.serverUrl == self.serverUrl {
- await self.reloadDataSource()
- }
- }
- // CREATE FOLDER
- case self.global.networkingStatusCreateFolder:
- if metadata.serverUrl == self.serverUrl, metadata.sessionSelector != self.global.selectorUploadAutoUpload {
- self.pushMetadata(metadata)
- }
- // RENAME
- case self.global.networkingStatusRename:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- // FAVORITE
- case self.global.networkingStatusFavorite:
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if self is NCFavorite {
- Task {
- await self.reloadDataSource()
- }
- } else if self.serverUrl == metadata.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- default:
- break
- }
- }
- }
-
- func transferReloadData(serverUrl: String?, status: Int?) {
- self.debouncer.call {
- if self.isSearchingMode {
- guard status != self.global.metadataStatusWaitDelete,
- status != self.global.metadataStatusWaitRename,
- status != self.global.metadataStatusWaitMove,
- status != self.global.metadataStatusWaitCopy,
- status != self.global.metadataStatusWaitFavorite else {
- return
- }
- self.networkSearch()
- } else if ( self.serverUrl == serverUrl) || serverUrl == nil {
- Task {
- await self.reloadDataSource()
- }
- }
- }
- }
-
- func transferRequestData(serverUrl: String?) {
- self.debouncer.call {
- if self.isSearchingMode {
- self.networkSearch()
- } else if ( self.serverUrl == serverUrl) || serverUrl == nil {
- Task {
- await self.getServerData()
- }
- }
- }
- }
-
- func transferCopy(metadata: tableMetadata, destination: String, error: NKError) {
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- if isSearchingMode {
- return networkSearch()
- }
-
- if metadata.serverUrl == self.serverUrl || destination == self.serverUrl {
- Task {
- await self.reloadDataSource()
- }
- }
- }
-
- func transferMove(metadata: tableMetadata, destination: String, error: NKError) {
- if error != .success {
- NCContentPresenter().showError(error: error)
- }
-
- if isSearchingMode {
- return networkSearch()
- }
-
- if metadata.serverUrl == self.serverUrl || destination == self.serverUrl {
- Task {
- await self.reloadDataSource()
- }
+ if let frame = tabBarController?.tabBar.frame {
+ tabBarSelect?.hostingController?.view.frame = frame
}
}
// MARK: - NotificationCenter
@objc func applicationWillResignActive(_ notification: NSNotification) {
- mainNavigationController?.resetPlusButtonAlpha()
+ self.resetPlusButtonAlpha()
+ self.refreshControl.endRefreshing()
}
- @objc func closeRichWorkspaceWebView() {
- Task {
- await self.reloadDataSource()
+ @objc func reloadAvatar(_ notification: NSNotification) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+ self.showTipAccounts()
}
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let error = userInfo["error"] as? NKError,
+ error.errorCode != global.errorNotModified else { return }
+ /// Magentacloud branding changes hide user account button on left navigation bar
+ setNavigationLeftItems()
}
- // MARK: - Layout
-
- func changeLayout(layoutForView: NCDBLayoutForView) {
- let homeServer = utilityFileSystem.getHomeServer(urlBase: session.urlBase, userId: session.userId)
- let numFoldersLayoutsForView = self.database.getLayoutsForView(keyStore: layoutForView.keyStore)?.count ?? 1
-
- func changeLayout(withSubFolders: Bool) {
- if self.layoutForView?.layout == layoutForView.layout {
- self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView, withSubFolders: withSubFolders)
- Task {
- await self.reloadDataSource()
- }
- return
- }
-
- self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView, withSubFolders: withSubFolders)
- layoutForView.layout = layoutForView.layout
- self.layoutType = layoutForView.layout
-
- collectionView.reloadData()
-
- switch layoutForView.layout {
- case global.layoutList:
- self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
- case global.layoutGrid:
- self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
- case global.layoutPhotoSquare, global.layoutPhotoRatio:
- self.collectionView.setCollectionViewLayout(self.mediaLayout, animated: true)
- default:
- break
- }
+ @objc func changeTheming(_ notification: NSNotification) {
+ self.reloadDataSource()
+ }
- self.collectionView.collectionViewLayout.invalidateLayout()
+ @objc func changeLayout(_ notification: NSNotification) {
+ guard let userInfo = notification.userInfo as NSDictionary?,
+ let account = userInfo["account"] as? String,
+ let serverUrl = userInfo["serverUrl"] as? String,
+ let layoutForView = userInfo["layoutForView"] as? NCDBLayoutForView,
+ account == session.account,
+ serverUrl == self.serverUrl
+ else { return }
- Task {
- await (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
- }
+ if self.layoutForView?.layout == layoutForView.layout {
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ self.reloadDataSource()
+ return
}
- if serverUrl == homeServer || numFoldersLayoutsForView == 1 {
- changeLayout(withSubFolders: false)
- } else {
- let alertController = UIAlertController(title: NSLocalizedString("_propagate_layout_", comment: ""), message: nil, preferredStyle: .alert)
+ self.layoutForView = self.database.setLayoutForView(layoutForView: layoutForView)
+ layoutForView.layout = layoutForView.layout
+ self.layoutType = layoutForView.layout
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
- changeLayout(withSubFolders: true)
- }))
- alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in
- changeLayout(withSubFolders: false)
- }))
+ collectionView.reloadData()
- self.present(alertController, animated: true)
+ switch layoutForView.layout {
+ case global.layoutList:
+ self.collectionView.setCollectionViewLayout(self.listLayout, animated: true)
+ case global.layoutGrid:
+ self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true)
+ case global.layoutPhotoSquare, global.layoutPhotoRatio:
+ self.collectionView.setCollectionViewLayout(self.mediaLayout, animated: true)
+ default:
+ break
}
+
self.collectionView.collectionViewLayout.invalidateLayout()
// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
@@ -934,16 +749,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
collectionView?.reloadData()
}
}
-
- @objc func updateShare(_ notification: NSNotification) {
- if isSearchingMode {
- networkSearch()
- } else {
- self.dataSource.removeAll()
- getServerData()
- }
- }
-
+
@objc func triggerProgressTask(_ notification: NSNotification) {
guard let userInfo = notification.userInfo as NSDictionary?,
let progressNumber = userInfo["progress"] as? NSNumber,
@@ -958,14 +764,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected))
-
- // HEADER
-// if self.headerMenuTransferView, transfer.session.contains("upload") {
-// self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
-// self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue)
-// }
- DispatchQueue.main.async {
+// DispatchQueue.main.async {
if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
if NCNetworking.shared.transferInForegorund?.ocId == ocId {
NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
@@ -975,7 +775,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue
} else {
- guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath,
+ guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId),
let cell = self.collectionView?.cellForItem(at: indexPath),
let cell = cell as? NCCellProtocol else { return }
if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
@@ -1007,8 +807,22 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
+// }
+ }
+
+ @objc func updateShare(_ notification: NSNotification) {
+ if isSearchingMode {
+ networkSearch()
+ } else {
+// self.dataSource.removeAll()
+ getServerData()
}
}
+
+ @objc func updateIcons() {
+ collectionView.reloadData()
+// reloadDataSource()
+ }
// MARK: - Layout
@@ -1017,45 +831,19 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func getNavigationTitle() -> String {
- let tblAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
- if let tblAccount,
- !tblAccount.alias.isEmpty {
- return tblAccount.alias
+ let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
+ if let tableAccount,
+ !tableAccount.alias.isEmpty {
+ return tableAccount.alias
}
return NCBrandOptions.shared.brand
}
- func accountSettingsDidDismiss(tblAccount: tableAccount?, controller: NCMainTabBarController?) { }
-
- @MainActor
- func showLoadingTitle() {
- // Don't show spinner on iPad root folder
- if UIDevice.current.userInterfaceIdiom == .pad,
- (self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session)) || self.serverUrl.isEmpty {
- return
- }
+ func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { }
- let spinner = UIActivityIndicatorView(style: .medium)
- spinner.startAnimating()
+ func resetPlusButtonAlpha(animated: Bool = true) { }
- let container = UIView()
- container.translatesAutoresizingMaskIntoConstraints = false
- container.addSubview(spinner)
-
- spinner.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- spinner.centerXAnchor.constraint(equalTo: container.centerXAnchor),
- spinner.centerYAnchor.constraint(equalTo: container.centerYAnchor)
- ])
-
- self.navigationItem.titleView = container
- }
-
- @MainActor
- func restoreDefaultTitle() {
- self.navigationItem.titleView = nil
- self.navigationItem.title = self.titleCurrentFolder
- }
+ func isHiddenPlusButton(_ isHidden: Bool) { }
// MARK: - Empty
@@ -1096,7 +884,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
searchController?.searchBar.alpha = 1
} else {
searchController?.searchBar.alpha = 0.3
-
}
}
@@ -1108,13 +895,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
isSearchingMode = true
self.providers?.removeAll()
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
+ self.reloadDataSource()
// TIP
dismissTip()
- //
- mainNavigationController?.hiddenPlusButton(true)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
@@ -1124,17 +907,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
- self.networking.cancelUnifiedSearchFiles()
-
+ NCNetworking.shared.cancelUnifiedSearchFiles()
self.isSearchingMode = false
self.literalSearch = ""
self.providers?.removeAll()
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
- //
- mainNavigationController?.hiddenPlusButton(false)
+ self.reloadDataSource()
}
// MARK: - TAP EVENT
@@ -1142,6 +920,10 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
}
+
+ func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender)
+ }
func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) {
tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender)
@@ -1150,7 +932,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) {
if isEditMode { return }
guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
- NCDownloadAction.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""])
appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue))
NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing)
@@ -1168,6 +949,16 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
+
+ func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) {
+ guard let metadata = self.database.getMetadataFromOcId(ocId) else { return }
+// toggleMenu(metadata: metadata, image: image)
+ Task {
+ await cancelSession(metadata: metadata)
+ }
+ }
+
+ func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func tapRichWorkspace(_ sender: Any) {
if let navigationController = UIStoryboard(name: "NCViewerRichWorkspace", bundle: nil).instantiateInitialViewController() as? UINavigationController {
@@ -1182,8 +973,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
- func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?, sender: Any?) {
- toggleMenu(metadata: metadata, image: image, sender: sender)
+ func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) {
+ toggleMenu(metadata: metadata, image: image)
}
func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {
@@ -1212,32 +1003,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
func tapButtonOrder(_ sender: Any) {
-
-// if let titleButtonHeader = NCKeychain().getTitleButtonHeader(account: session.account), !titleButtonHeader.isEmpty {
-// layoutForView?.titleButtonHeader = titleButtonHeader
-// }
-// NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView!)
-
let sortMenu = NCSortMenu()
sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
}
- func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapButtonTransfer(_ sender: Any) { }
- func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+ func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
- func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) { }
+ func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) {
+ }
+ func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
+
+ func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
func longPressMoreGridItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
- func tapButtonTransfer(_ sender: Any) { }
-
- func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { }
-
@objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) {
openMenuItems(with: nil, gestureRecognizer: gestureRecognizer)
}
@@ -1259,7 +1046,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
becomeFirstResponder()
if !serverUrl.isEmpty {
- listMenuItems.append(UIMenuItem(title: NSLocalizedString("_paste_file_", comment: ""), action: #selector(pasteFilesMenu(_:))))
+ listMenuItems.append(UIMenuItem(title: NSLocalizedString("_paste_file_", comment: ""), action: #selector(pasteFilesMenu)))
}
if !listMenuItems.isEmpty {
@@ -1268,36 +1055,41 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
+ // MARK: - Transfer Delegate
+
+ func transferProgressDidUpdate(progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String) { }
+
+ func tranferChange(status: String, metadata: tableMetadata, error: NKError) { }
+
// MARK: - Menu Item
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
- if #selector(pasteFilesMenu(_:)) == action {
+
+ if #selector(pasteFilesMenu) == action {
if !UIPasteboard.general.items.isEmpty, !(metadataFolder?.e2eEncrypted ?? false) {
return true
}
- } else if #selector(copyMenuFile(_:)) == action {
+ } else if #selector(copyMenuFile) == action {
return true
- } else if #selector(moveMenuFile(_:)) == action {
+ } else if #selector(moveMenuFile) == action {
return true
}
return false
}
- @objc func pasteFilesMenu(_ sender: Any?) {
- Task {
- await NCDownloadAction.shared.pastePasteboard(serverUrl: serverUrl, account: session.account, controller: self.controller)
- }
+ @objc func pasteFilesMenu() {
+ NCActionCenter.shared.pastePasteboard(serverUrl: serverUrl, account: session.account, controller: self.controller)
}
// MARK: - DataSource
- @objc func reloadDataSource() async {
+ @objc func reloadDataSource() {
// get auto upload folder
autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account)
-
+
// get layout for view
layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl)
// set GroupField for Grid
@@ -1306,32 +1098,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
} else {
groupByField = "name"
}
-
if isSearchingMode {
isDirectoryEncrypted = false
} else {
isDirectoryEncrypted = NCUtilityFileSystem().isDirectoryE2EE(session: session, serverUrl: serverUrl)
- if isRecommendationActived {
- Task.detached {
- await self.networking.createRecommendations(session: self.session, serverUrl: self.serverUrl, collectionView: self.collectionView)
- }
- }
}
- UIView.transition(with: self.collectionView,
- duration: 0.20,
- options: .transitionCrossDissolve,
- animations: { self.collectionView.reloadData() },
- completion: nil)
-
- (self.navigationController as? NCMainNavigationController)?.updateRightMenu()
+ DispatchQueue.main.async {
+// UIView.transition(with: self.collectionView,
+// duration: 0.20,
+// options: .transitionCrossDissolve,
+// animations: { self.collectionView.reloadData() },
+// completion: nil)
+//
+// (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems()
self.refreshControl.endRefreshing()
self.collectionView.reloadData()
self.setNavigationRightItems()
}
}
- func getServerData(forced: Bool = false) async { }
+ func getServerData() {
+ }
@objc func networkSearch() {
guard !networkSearchInProgress else {
@@ -1340,68 +1128,45 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
guard !session.account.isEmpty,
let literalSearch = literalSearch,
!literalSearch.isEmpty else {
- return
+ return self.refreshControl.endRefreshing()
}
- let capabilities = NCNetworking.shared.capabilities[session.account] ?? NKCapabilities.Capabilities()
self.networkSearchInProgress = true
self.dataSource.removeAll()
- Task {
- await self.reloadDataSource()
- }
-
- if capabilities.serverVersionMajor >= global.nextcloudVersion20 {
- self.networking.unifiedSearchFiles(literal: literalSearch, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
- } providers: { account, searchProviders in
+ self.refreshControl.beginRefreshing()
+ self.reloadDataSource()
+
+ if NCCapabilities.shared.getCapabilities(account: session.account).capabilityServerVersionMajor >= global.nextcloudVersion20 {
+ NCNetworking.shared.unifiedSearchFiles(literal: literalSearch, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
+ } providers: { _, searchProviders in
self.providers = searchProviders
self.searchResults = []
- self.dataSource = NCCollectionViewDataSource(metadatas: [],
- layoutForView: self.layoutForView,
- providers: self.providers,
- searchResults: self.searchResults,
- account: account)
+ self.dataSource = NCCollectionViewDataSource(metadatas: [], layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults)
} update: { _, _, searchResult, metadatas in
guard let metadatas, !metadatas.isEmpty, self.isSearchingMode, let searchResult else { return }
- self.networking.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
+ NCNetworking.shared.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
} completion: { _, _ in
- Task {
- await self.reloadDataSource()
- }
+ self.refreshControl.endRefreshing()
+ self.reloadDataSource()
self.networkSearchInProgress = false
}
} else {
- self.networking.searchFiles(literal: literalSearch, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
+ NCNetworking.shared.searchFiles(literal: literalSearch, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
} completion: { metadatasSearch, error in
- Task {
- guard let metadatasSearch,
- error == .success,
- self.isSearchingMode
- else {
- self.networkSearchInProgress = false
- await self.reloadDataSource()
- return
- }
- let ocId = metadatasSearch.map { $0.ocId }
- let metadatas = await self.database.getMetadatasAsync(predicate: NSPredicate(format: "ocId IN %@", ocId),
- withLayout: self.layoutForView,
- withAccount: self.session.account)
-
- self.dataSource = NCCollectionViewDataSource(metadatas: metadatas,
- layoutForView: self.layoutForView,
- providers: self.providers,
- searchResults: self.searchResults,
- account: self.session.account)
- self.networkSearchInProgress = false
- await self.reloadDataSource()
+ DispatchQueue.main.async {
+ self.refreshControl.endRefreshing()
+ self.reloadDataSource()
}
+ guard let metadatasSearch, error == .success, self.isSearchingMode else { return }
+ let ocId = metadatasSearch.map { $0.ocId }
+ let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: self.layoutForView)
+
+ self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults)
+ self.networkSearchInProgress = false
}
}
}
@@ -1412,11 +1177,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
metadataForSection.unifiedSearchInProgress = true
self.collectionView?.reloadData()
- self.networking.unifiedSearchFilesProvider(id: lastSearchResult.id, term: term, limit: 5, cursor: cursor, account: session.account) { task in
- self.searchDataSourceTask = task
- Task {
- await self.reloadDataSource()
- }
+ NCNetworking.shared.unifiedSearchFilesProvider(id: lastSearchResult.id, term: term, limit: 5, cursor: cursor, account: session.account) { task in
+ self.dataSourceTask = task
+ self.reloadDataSource()
} completion: { _, searchResult, metadatas, error in
if error != .success {
NCContentPresenter().showError(error: error)
@@ -1435,15 +1198,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - Push metadata
func pushMetadata(_ metadata: tableMetadata) {
- guard let navigationCollectionViewCommon = self.controller?.navigationCollectionViewCommon else {
- return
- }
- let serverUrlPush = utilityFileSystem.createServerUrl(serverUrl: metadata.serverUrl, fileName: metadata.fileName)
-
- // Set Last Opening Date
- Task {
- await database.setDirectoryLastOpeningDateAsync(ocId: metadata.ocId)
- }
+ guard let navigationCollectionViewCommon = self.controller?.navigationCollectionViewCommon else { return }
+ let serverUrlPush = utilityFileSystem.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)
if let viewController = navigationCollectionViewCommon.first(where: { $0.navigationController == self.navigationController && $0.serverUrl == serverUrlPush})?.viewController, viewController.isViewLoaded {
navigationController?.pushViewController(viewController, animated: true)
@@ -1459,38 +1215,55 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
}
}
}
-
+
func pushViewController(viewController: UIViewController) {
if pushed { return }
pushed = true
navigationController?.pushViewController(viewController, animated: true)
}
-
+
// MARK: - Header size
-// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
-// heightHeaderRecommendations: CGFloat,
-// heightHeaderSection: CGFloat) {
-// var heightHeaderRichWorkspace: CGFloat = 0
-// var heightHeaderRecommendations: CGFloat = 0
-// var heightHeaderSection: CGFloat = 0
+// func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
+// var headerRichWorkspace: CGFloat = 0
//
-// if showDescription,
-// !isSearchingMode,
-// let richWorkspaceText = self.richWorkspaceText,
-// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
-// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
-// }
+// func getHeaderHeight() -> CGFloat {
+// var size: CGFloat = 0
//
-// if isRecommendationActived,
-// !isSearchingMode,
-// NCKeychain().showRecommendedFiles,
-// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
-// heightHeaderRecommendations = self.heightHeaderRecommendations
-// heightHeaderSection = self.heightHeaderSection
+// if isHeaderMenuTransferViewEnabled() != nil {
+// if !isSearchingMode {
+// size += global.heightHeaderTransfer
+// }
+// }
+// if headerMenuButtonsView {
+// size += NCGlobal.shared.heightButtonsView
+// }
+// return size
// }
//
+//// func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat,
+//// heightHeaderRecommendations: CGFloat,
+//// heightHeaderSection: CGFloat) {
+//// var heightHeaderRichWorkspace: CGFloat = 0
+//// var heightHeaderRecommendations: CGFloat = 0
+//// var heightHeaderSection: CGFloat = 0
+////
+//// if showDescription,
+//// !isSearchingMode,
+//// let richWorkspaceText = self.richWorkspaceText,
+//// !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty {
+//// heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6
+//// }
+////
+//// if isRecommendationActived,
+//// !isSearchingMode,
+//// NCKeychain().showRecommendedFiles,
+//// !self.database.getRecommendedFiles(account: self.session.account).isEmpty {
+//// heightHeaderRecommendations = self.heightHeaderRecommendations
+//// heightHeaderSection = self.heightHeaderSection
+//// }
+//
// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
// if section == 0 {
// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
@@ -1499,6 +1272,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// }
// } else {
// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
+////
+//// if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 {
+//// if section == 0 {
+//// return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection)
+//// } else {
+//// return (0, 0, self.heightHeaderSection)
+//// }
+//// } else {
+//// return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection)
// }
// }
@@ -1522,8 +1304,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
} else if isEditMode || (isLandscape && isIphone) {
return CGSize.zero
} else {
- let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: section)
- height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderSection
+ let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section)
+ height = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection
}
return CGSize(width: collectionView.frame.width, height: height)
@@ -1532,25 +1314,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
// MARK: - Footer size
func sizeForFooterInSection(section: Int) -> CGSize {
- guard let controller else {
- return CGSize.zero
- }
let sections = dataSource.numberOfSections()
- let bottomAreaInsets: CGFloat = controller.tabBar.safeAreaInsets.bottom == 0 ? 34 : 0
- let height = controller.tabBar.frame.height + bottomAreaInsets
-
- if isEditMode {
- return CGSize(width: collectionView.frame.width, height: 90 + height)
- }
-
- if isSearchingMode {
- return CGSize(width: collectionView.frame.width, height: 50)
- }
+ let metadataForSection = self.dataSource.getMetadataForSection(section)
+ let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false
+ let metadatasCount: Int = metadataForSection?.lastSearchResult?.entries.count ?? 0
+ var size = CGSize(width: collectionView.frame.width, height: 0)
if section == sections - 1 {
- return CGSize(width: collectionView.frame.width, height: height)
+ size.height += 85
} else {
- return CGSize(width: collectionView.frame.width, height: 0)
+ size.height += 1
+ }
+
+ if isSearchingMode && isPaginated && metadatasCount > 0 {
+ size.height += 30
}
+ return size
}
}
From 0c9b02b60696c0acaff5bb187adccdb8f834cf16 Mon Sep 17 00:00:00 2001
From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com>
Date: Mon, 23 Oct 2023 14:24:51 +0530
Subject: [PATCH 052/177] NMC 1935 - Notification screen customisation
---
.../NCNotificationText.swift | 85 ++++++++
.../Notification/NCNotification.storyboard | 202 ++++++++----------
iOSClient/Notification/NCNotification.swift | 60 ++----
3 files changed, 193 insertions(+), 154 deletions(-)
create mode 100644 Tests/NextcloudUnitTests/NCNotificationText.swift
diff --git a/Tests/NextcloudUnitTests/NCNotificationText.swift b/Tests/NextcloudUnitTests/NCNotificationText.swift
new file mode 100644
index 0000000000..1a7e0b2345
--- /dev/null
+++ b/Tests/NextcloudUnitTests/NCNotificationText.swift
@@ -0,0 +1,85 @@
+//
+// NCNotificationText.swift
+// NextcloudUnitTests
+//
+// Created by Amrut Waghmare on 18/10/23.
+// Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+@testable import Nextcloud
+import XCTest
+import NextcloudKit
+
+class NCNotificationText: XCTestCase {
+ var viewController : NCNotification!
+
+ override func setUpWithError() throws {
+ // Step 1. Create an instance of UIStoryboard
+ let storyboard = UIStoryboard(name: "NCNotification", bundle: nil)
+ // Step 2. Instantiate UIViewController with Storyboard ID
+ viewController = storyboard.instantiateViewController(withIdentifier: "NCNotification.storyboard") as? NCNotification
+
+ // Step 3. Make the viewDidLoad() execute.
+ viewController.loadViewIfNeeded()
+ }
+
+ override func tearDownWithError() throws {
+ viewController = nil
+ }
+
+ //Test that a cell with the correct reuse identifier is dequeued
+ func testTableViewCellDequeue() {
+ let notification = NKNotifications()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertNotNil(cell)
+ XCTAssertEqual(cell?.reuseIdentifier, "Cell")
+ }
+
+ //Test that the cell's icon is set image
+ func testTableViewCellIcon() {
+ let notification = NKNotifications()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertNotNil(cell?.icon.image)
+ }
+
+ //Test that the cell's primary and secondary buttons are set up correctly
+ func testTableViewCellButtons() {
+ let notification = NKNotifications()
+ notification.actions = Data("[{\"label\":\"OK\",\"primary\":true},{\"label\":\"Cancel\",\"primary\":false}]".utf8)
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertEqual(cell?.primary.title(for: .normal), "OK")
+ XCTAssertEqual(cell?.secondary.title(for: .normal), "Cancel")
+ }
+
+ //Test that the cell's date label is set correctly
+ func testTableViewCellDate() {
+ let notification = NKNotifications()
+ notification.date = NSDate()
+ viewController.notifications = [notification]
+ let tableView = UITableView()
+ tableView.register(NCNotificationCell.self, forCellReuseIdentifier: "Cell")
+ let indexPath = IndexPath(row: 0, section: 0)
+ let cell = viewController.tableView(tableView, cellForRowAt: indexPath) as? NCNotificationCell
+ XCTAssertEqual(cell?.date.text, "less than a minute ago")
+ }
+
+ //Test with a color that is image not nil
+ func testImageNotNil() {
+ let color = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
+ let image = UIImage().imageColor(color)
+ XCTAssertNotNil(image, "Image should not be nil.")
+
+ }
+}
diff --git a/iOSClient/Notification/NCNotification.storyboard b/iOSClient/Notification/NCNotification.storyboard
index fb122ec81a..a89ba02d24 100644
--- a/iOSClient/Notification/NCNotification.storyboard
+++ b/iOSClient/Notification/NCNotification.storyboard
@@ -1,193 +1,163 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-