From 396a06f5781731300b3f2081909e48d72d5d28ce Mon Sep 17 00:00:00 2001 From: Dwayne Harris Date: Sun, 11 Nov 2018 14:39:27 -0800 Subject: [PATCH] Add Account Field table --- elpha-ios.xcodeproj/project.pbxproj | 10 +- .../AbstractStatusTableViewController.swift | 13 +- elpha-ios/AccountTableViewController.swift | 91 +++++-- elpha-ios/AttachmentManager.swift | 43 +-- elpha-ios/Base.lproj/Main.storyboard | 244 ++++++++++-------- elpha-ios/FieldTableViewCell.swift | 14 + elpha-ios/MastodonDataManager.swift | 6 +- elpha-ios/StatusTableViewController.swift | 2 +- elpha-ios/String+HtmlAttributed.swift | 4 + elpha-ios/TimelineTableViewController.swift | 2 +- elpha-ios/UITextViewFixed.swift | 2 +- 11 files changed, 284 insertions(+), 147 deletions(-) create mode 100644 elpha-ios/FieldTableViewCell.swift diff --git a/elpha-ios.xcodeproj/project.pbxproj b/elpha-ios.xcodeproj/project.pbxproj index 8f6e90d..ca28c98 100644 --- a/elpha-ios.xcodeproj/project.pbxproj +++ b/elpha-ios.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 152FBCE621978C4A0079B3E8 /* CellHeightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FBCE521978C4A0079B3E8 /* CellHeightManager.swift */; }; 152FBCED219799FC0079B3E8 /* FLAnimatedImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */; }; 152FBCEE219799FC0079B3E8 /* FLAnimatedImage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 152FBCEC219799E50079B3E8 /* FLAnimatedImage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 152FBCF2219818AD0079B3E8 /* FieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152FBCF1219818AD0079B3E8 /* FieldTableViewCell.swift */; }; 1539509121894A38009BA6E7 /* AlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1539509021894A38009BA6E7 /* AlertManager.swift */; }; 156FF015217289380074D9CA /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156FF014217289380074D9CA /* AccountTableViewCell.swift */; }; 156FF0312174797E0074D9CA /* StatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156FF0302174797E0074D9CA /* StatusTableViewController.swift */; }; @@ -249,6 +250,7 @@ 152FBCD1219682E80079B3E8 /* AbstractStatusTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractStatusTableViewController.swift; sourceTree = ""; }; 152FBCE521978C4A0079B3E8 /* CellHeightManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellHeightManager.swift; sourceTree = ""; }; 152FBCE7219799E50079B3E8 /* FLAnimatedImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FLAnimatedImage.xcodeproj; path = Frameworks/FLAnimatedImage/FLAnimatedImage.xcodeproj; sourceTree = ""; }; + 152FBCF1219818AD0079B3E8 /* FieldTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldTableViewCell.swift; sourceTree = ""; }; 1539509021894A38009BA6E7 /* AlertManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertManager.swift; sourceTree = ""; }; 156FF014217289380074D9CA /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = ""; }; 156FF0302174797E0074D9CA /* StatusTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewController.swift; sourceTree = ""; }; @@ -354,17 +356,18 @@ name = "Reusable Views"; sourceTree = ""; }; - 151AD4AE2166DD3500F07403 /* Views */ = { + 151AD4AE2166DD3500F07403 /* Table View Cells */ = { isa = PBXGroup; children = ( 156FF014217289380074D9CA /* AccountTableViewCell.swift */, + 152FBCF1219818AD0079B3E8 /* FieldTableViewCell.swift */, 159048AE214F5015004F4014 /* InstancesTableViewCell.swift */, 156FF04E2175CDBC0074D9CA /* MainStatusTableViewCell.swift */, 156FF050217683270074D9CA /* StatusTableViewCell.swift */, 15BB72AA2171A8D4002F1FA4 /* TimelinesTableViewCell.swift */, 159026AD2162CF5600D362DD /* TimelineTableViewCell.swift */, ); - name = Views; + name = "Table View Cells"; sourceTree = ""; }; 151AD4AF2166DDA000F07403 /* Extensions */ = { @@ -471,7 +474,7 @@ 151AD4AB2166DCEE00F07403 /* Storyboards */, 152FBCE4219789450079B3E8 /* Abstract View Controllers */, 15960E782132383600C38CE9 /* View Controllers */, - 151AD4AE2166DD3500F07403 /* Views */, + 151AD4AE2166DD3500F07403 /* Table View Cells */, ); path = "elpha-ios"; sourceTree = ""; @@ -733,6 +736,7 @@ files = ( 1539509121894A38009BA6E7 /* AlertManager.swift in Sources */, 152FB0FA218ADDD0001D6574 /* AttachmentViewController.swift in Sources */, + 152FBCF2219818AD0079B3E8 /* FieldTableViewCell.swift in Sources */, 159048AF214F5015004F4014 /* InstancesTableViewCell.swift in Sources */, 15960E84213774FC00C38CE9 /* InstancesTableViewController.swift in Sources */, 15960E7021321FA500C38CE9 /* Elpha.xcdatamodeld in Sources */, diff --git a/elpha-ios/AbstractStatusTableViewController.swift b/elpha-ios/AbstractStatusTableViewController.swift index b27cd37..e5bda41 100644 --- a/elpha-ios/AbstractStatusTableViewController.swift +++ b/elpha-ios/AbstractStatusTableViewController.swift @@ -11,10 +11,21 @@ import SafariServices class AbstractStatusTableViewController: UITableViewController, StatusViewDelegate { let fetchLimit = 20 - var loading: Bool = false var currentPaginationContext: String = "" var cellHeightsDictionary: NSMutableDictionary = [:] + var loading: Bool = false { + didSet { + DispatchQueue.main.async { + if self.loading { + self.refreshControl?.beginRefreshing() + } else { + self.refreshControl?.endRefreshing() + } + } + } + } + func accountTapped(account: AccountMO) { let storyboard = UIStoryboard(name: "Main", bundle: nil) diff --git a/elpha-ios/AccountTableViewController.swift b/elpha-ios/AccountTableViewController.swift index bf1f09b..9e88303 100644 --- a/elpha-ios/AccountTableViewController.swift +++ b/elpha-ios/AccountTableViewController.swift @@ -11,20 +11,73 @@ import CoreData import UIKit import SafariServices +class FieldTableViewController: NSObject, UITableViewDelegate, UITableViewDataSource { + var fields: [AccountField]? = nil + + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return fields?.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "FieldTableViewCell", for: indexPath) as? FieldTableViewCell else { + fatalError("Unable to find reusable cell") + } + + if let fields = fields { + let field = fields[indexPath.row] + + cell.nameLabel.text = field.name + + do { + let htmlString = """ + + \(field.value) + """ + + if let data = htmlString.data(using: String.Encoding.utf16) { + cell.valueLabel.attributedText = try NSAttributedString( + data: data, + options: [.documentType: NSAttributedString.DocumentType.html], + documentAttributes: nil + ) + } + } catch { + print("\(error)") + } + } + + return cell + } +} + class AccountTableViewController: AbstractStatusTableViewController { @IBOutlet var headerView: UIView! @IBOutlet var headerImageView: UIImageView! @IBOutlet var avatarImageView: UIImageView! @IBOutlet var displayNameLabel: UILabel! @IBOutlet var usernameLabel: UILabel! - @IBOutlet var contentLabel: UILabel! @IBOutlet var statusesLabel: UILabel! @IBOutlet var followingLabel: UILabel! @IBOutlet var followersLabel: UILabel! @IBOutlet var statusTypeSegmentedControl: UISegmentedControl! + @IBOutlet var contentTextView: UITextViewFixed! + @IBOutlet var fieldTableView: UITableView! var fetchedResultsController: NSFetchedResultsController? = nil var account: AccountMO? = nil + var fieldTableViewController: FieldTableViewController? = nil override var currentPaginationContext: String { get { @@ -32,12 +85,7 @@ class AccountTableViewController: AbstractStatusTableViewController { return "" } - switch statusTypeSegmentedControl.selectedSegmentIndex { - case 3: - return "timeline:favorites" - case let index: - return "account:\(account.acct!):\(index)" - } + return "account:\(account.acct!):\(statusTypeSegmentedControl.selectedSegmentIndex)" } set { @@ -71,18 +119,35 @@ class AccountTableViewController: AbstractStatusTableViewController { usernameLabel.text = "@\(account.acct!)" if let note = account.note { - contentLabel.attributedText = note.htmlAttributed(size: 15, centered: true) + contentTextView.attributedText = note.htmlAttributed(size: 15, centered: true) } statusesLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.statusesCount), number: .decimal) followingLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followingCount), number: .decimal) followersLabel.text = NumberFormatter.localizedString(from: NSNumber(value: account.followersCount), number: .decimal) + + if let fields = account.fields { + fieldTableViewController!.fields = fields + + fieldTableView.dataSource = fieldTableViewController + fieldTableView.delegate = fieldTableViewController + + fieldTableView.isHidden = false + } else { + fieldTableView.isHidden = true + } } override func viewDidLoad() { super.viewDidLoad() refreshControl?.addTarget(self, action: #selector(self.fetch), for: .valueChanged) + fieldTableViewController = FieldTableViewController() + + avatarImageView.layer.shadowColor = UIColor.black.cgColor + avatarImageView.layer.shadowOpacity = 0.8 + avatarImageView.layer.shadowOffset = CGSize.zero + avatarImageView.layer.shadowRadius = 10 if self.account == nil { if let session = AuthenticationManager.session { @@ -213,12 +278,6 @@ class AccountTableViewController: AbstractStatusTableViewController { pagination: pagination, completion: requestCompletion ) - case 3: - MastodonAPI.favorites( - limit: fetchLimit, - pagination: pagination, - completion: requestCompletion - ) default: MastodonAPI.statuses( accountID: account.id!, @@ -247,8 +306,6 @@ extension AccountTableViewController: NSFetchedResultsControllerDelegate { request.predicate = NSPredicate(format: "account == %@", account) case 2: request.predicate = NSPredicate(format: "account == %@ AND attachments.@count > 0", account) - case 3: - request.predicate = NSPredicate(format: "favorited == YES") default: request.predicate = NSPredicate(format: "account == %@ AND inReplyToID == nil", account) } @@ -284,7 +341,7 @@ extension AccountTableViewController: NSFetchedResultsControllerDelegate { } } - override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return CellHeightManager.get(status: fetchedResultsController?.object(at: indexPath)) } diff --git a/elpha-ios/AttachmentManager.swift b/elpha-ios/AttachmentManager.swift index 948b963..4b88a5b 100644 --- a/elpha-ios/AttachmentManager.swift +++ b/elpha-ios/AttachmentManager.swift @@ -7,6 +7,7 @@ // import AlamofireImage +import FLAnimatedImage import UIKit protocol AttachmentManagerDelegate { @@ -16,6 +17,10 @@ protocol AttachmentManagerDelegate { class AttachmentManager: NSObject { var delegate: AttachmentManagerDelegate? = nil + static private var downloader = ImageDownloader() + static private var imageCache = AutoPurgingImageCache() + static private let placeholderImage = UIImage(named: "Placeholder") + @objc func attachmentTapped(_ gestureRecognizer: UITapGestureRecognizer) { if let delegate = delegate, let name = gestureRecognizer.name { delegate.attachmentTapped(index: Int(name)!) @@ -40,8 +45,7 @@ class AttachmentManager: NSObject { let filter = AspectScaledToFillSizeFilter(size: CGSize(width: view.frame.width, height: view.frame.width)) let attachment = attachments.firstObject as! AttachmentMO - let imageView = UIImageView() - imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + let imageView = FLAnimatedImageView() let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(attachmentTapped)) tapGestureRecognizer.delegate = self @@ -51,11 +55,20 @@ class AttachmentManager: NSObject { imageView.addGestureRecognizer(tapGestureRecognizer) imageView.isUserInteractionEnabled = true + imageView.contentMode = UIImageView.ContentMode.center + imageView.image = AttachmentManager.placeholderImage - imageView.af_setImage( - withURL: attachment.url!, - filter: filter - ) + let request = URLRequest(url: attachment.url!) + AttachmentManager.downloader.download(request, filter: filter) { response in + imageView.contentMode = UIImageView.ContentMode.scaleAspectFill + + switch attachment.type { + case "gif": + imageView.animatedImage = FLAnimatedImage(animatedGIFData: response.data) + default: + imageView.image = response.result.value + } + } view.addSubview(imageView) imageView.translatesAutoresizingMaskIntoConstraints = false @@ -70,8 +83,8 @@ class AttachmentManager: NSObject { let filter = AspectScaledToFillSizeFilter(size: CGSize(width: halfWidth, height: view.frame.width)) let imageViews = [ - UIImageView(), - UIImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), ] for (index, imageView) in imageViews.enumerated() { @@ -110,9 +123,9 @@ class AttachmentManager: NSObject { let secondaryFilter = AspectScaledToFillSizeFilter(size: CGSize(width: halfWidth, height: halfWidth)) let imageViews = [ - UIImageView(), - UIImageView(), - UIImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), ] for (index, imageView) in imageViews.enumerated() { @@ -159,10 +172,10 @@ class AttachmentManager: NSObject { let filter = AspectScaledToFillSizeFilter(size: CGSize(width: halfWidth, height: halfWidth)) let imageViews = [ - UIImageView(), - UIImageView(), - UIImageView(), - UIImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), + FLAnimatedImageView(), ] for (index, imageView) in imageViews.enumerated() { diff --git a/elpha-ios/Base.lproj/Main.storyboard b/elpha-ios/Base.lproj/Main.storyboard index 832b575..405dcc4 100644 --- a/elpha-ios/Base.lproj/Main.storyboard +++ b/elpha-ios/Base.lproj/Main.storyboard @@ -1,6 +1,6 @@ - + @@ -16,17 +16,17 @@ - + - + @@ -37,7 +37,7 @@ - +