Browse Source

Development

master
Dwayne Harris 6 years ago
parent
commit
f6ef746502
  1. 12
      elpha-ios.xcodeproj/project.pbxproj
  2. 12
      elpha-ios/Assets.xcassets/More.imageset/Contents.json
  3. BIN
      elpha-ios/Assets.xcassets/More.imageset/more-vertical.pdf
  4. 12
      elpha-ios/Assets.xcassets/Refresh CCW.imageset/Contents.json
  5. BIN
      elpha-ios/Assets.xcassets/Refresh CCW.imageset/refresh-ccw.pdf
  6. 18
      elpha-ios/AttachmentsManager.swift
  7. 12
      elpha-ios/AttachmentsView.swift
  8. 2
      elpha-ios/AuthenticationManager.swift
  9. 2
      elpha-ios/Base.lproj/Main.storyboard
  10. 24
      elpha-ios/MastodonDataManager.swift
  11. 35
      elpha-ios/NewStatusesView.swift
  12. 42
      elpha-ios/NewStatusesView.xib
  13. 2
      elpha-ios/TimelineTableViewCell.swift
  14. 220
      elpha-ios/TimelineTableViewController.swift
  15. 32
      elpha-ios/TimelinesNavigationController.swift

12
elpha-ios.xcodeproj/project.pbxproj

@ -19,7 +19,6 @@
157405B12151A5DA00EEAAEB /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 157405AF2151A5DA00EEAAEB /* README.md */; };
157405B42151A93E00EEAAEB /* InstancesDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157405B32151A93E00EEAAEB /* InstancesDataManager.swift */; };
157405D1215890D700EEAAEB /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 157405C3215890BC00EEAAEB /* Alamofire.framework */; };
1574147321696DB400C841BD /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1574147221696DB400C841BD /* AttachmentsView.swift */; };
1574148D2169AD0100C841BD /* AttachmentsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1574148C2169AD0100C841BD /* AttachmentsManager.swift */; };
159026AE2162CF5600D362DD /* TimelineTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159026AD2162CF5600D362DD /* TimelineTableViewCell.swift */; };
159026D02163069600D362DD /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159026CF2163069600D362DD /* Date+TimeAgo.swift */; };
@ -42,6 +41,8 @@
15A79B20215B439A007A326E /* OAuthSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15A79B13215B438C007A326E /* OAuthSwift.framework */; };
15A79B2E215C63B6007A326E /* AlamofireImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1517EA842159D72200DE80D6 /* AlamofireImage.framework */; };
15A79B43215EB959007A326E /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A79B42215EB959007A326E /* CoreDataManager.swift */; };
15C91A02216AB2D600D97DC3 /* NewStatusesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 15C91A01216AB2D600D97DC3 /* NewStatusesView.xib */; };
15C91A04216AB32500D97DC3 /* NewStatusesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C91A03216AB32500D97DC3 /* NewStatusesView.swift */; };
15F9981721629965009E58DA /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F9981621629965009E58DA /* TimelineTableViewController.swift */; };
15F998352162C0E8009E58DA /* MastodonDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F998342162C0E8009E58DA /* MastodonDataManager.swift */; };
/* End PBXBuildFile section */
@ -282,7 +283,6 @@
157405AF2151A5DA00EEAAEB /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
157405B32151A93E00EEAAEB /* InstancesDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesDataManager.swift; sourceTree = "<group>"; };
157405B7215890BC00EEAAEB /* Alamofire.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Alamofire.xcodeproj; path = Frameworks/Alamofire/Alamofire.xcodeproj; sourceTree = "<group>"; };
1574147221696DB400C841BD /* AttachmentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
1574148C2169AD0100C841BD /* AttachmentsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsManager.swift; sourceTree = "<group>"; };
159026AD2162CF5600D362DD /* TimelineTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewCell.swift; sourceTree = "<group>"; };
159026CF2163069600D362DD /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
@ -306,6 +306,8 @@
15A79AE7215B3CC5007A326E /* MastodonKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MastodonKit.xcodeproj; path = Frameworks/MastodonKit/MastodonKit.xcodeproj; sourceTree = "<group>"; };
15A79B08215B438C007A326E /* OAuthSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OAuthSwift.xcodeproj; path = Frameworks/OAuthSwift/OAuthSwift.xcodeproj; sourceTree = "<group>"; };
15A79B42215EB959007A326E /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
15C91A01216AB2D600D97DC3 /* NewStatusesView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NewStatusesView.xib; sourceTree = "<group>"; };
15C91A03216AB32500D97DC3 /* NewStatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewStatusesView.swift; sourceTree = "<group>"; };
15F9981621629965009E58DA /* TimelineTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableViewController.swift; sourceTree = "<group>"; };
15F998342162C0E8009E58DA /* MastodonDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonDataManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -368,6 +370,8 @@
151AD4AD2166DD1B00F07403 /* Reusable Views */ = {
isa = PBXGroup;
children = (
15C91A01216AB2D600D97DC3 /* NewStatusesView.xib */,
15C91A03216AB32500D97DC3 /* NewStatusesView.swift */,
);
name = "Reusable Views";
sourceTree = "<group>";
@ -375,7 +379,6 @@
151AD4AE2166DD3500F07403 /* Views */ = {
isa = PBXGroup;
children = (
1574147221696DB400C841BD /* AttachmentsView.swift */,
159048AE214F5015004F4014 /* InstancesTableViewCell.swift */,
159026AD2162CF5600D362DD /* TimelineTableViewCell.swift */,
);
@ -768,6 +771,7 @@
buildActionMask = 2147483647;
files = (
15960E67213145E200C38CE9 /* LaunchScreen.storyboard in Resources */,
15C91A02216AB2D600D97DC3 /* NewStatusesView.xib in Resources */,
15960E64213145E200C38CE9 /* Assets.xcassets in Resources */,
157405B12151A5DA00EEAAEB /* README.md in Resources */,
15960E62213145E100C38CE9 /* Main.storyboard in Resources */,
@ -793,6 +797,7 @@
15960E7E21329FED00C38CE9 /* AuthenticateViewController.swift in Sources */,
15960E5B213145E100C38CE9 /* AppDelegate.swift in Sources */,
15960E7721322C6F00C38CE9 /* Configuration.swift in Sources */,
15C91A04216AB32500D97DC3 /* NewStatusesView.swift in Sources */,
15960E7521322BF800C38CE9 /* KeychainWrapper.swift in Sources */,
157405A82150588A00EEAAEB /* InstanceViewController.swift in Sources */,
15960E7321322BC700C38CE9 /* KeychainItemAccessibility.swift in Sources */,
@ -801,7 +806,6 @@
1574148D2169AD0100C841BD /* AttachmentsManager.swift in Sources */,
15960E822136668500C38CE9 /* TimelinesNavigationController.swift in Sources */,
159026AE2162CF5600D362DD /* TimelineTableViewCell.swift in Sources */,
1574147321696DB400C841BD /* AttachmentsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

12
elpha-ios/Assets.xcassets/More.imageset/Contents.json

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "more-vertical.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
elpha-ios/Assets.xcassets/More.imageset/more-vertical.pdf

12
elpha-ios/Assets.xcassets/Refresh CCW.imageset/Contents.json

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "refresh-ccw.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
elpha-ios/Assets.xcassets/Refresh CCW.imageset/refresh-ccw.pdf

18
elpha-ios/AttachmentsManager.swift

@ -10,13 +10,13 @@ import AlamofireImage
import UIKit
class AttachmentsManager {
static func setupAttachmentsView(_ view: AttachmentsView, withAttachments attachments: NSOrderedSet?) {
static func setupAttachmentsView(_ view: UIView, withAttachments attachments: NSOrderedSet?) {
guard let attachments = attachments, attachments.count > 0 else {
return
}
let placeholderImage = UIImage(named: "Help")
let halfWidth = (view.frame.width / 2) - 2
let halfWidth = (view.frame.width / 2) - 1
for subview in view.subviews as [UIView] {
subview.removeFromSuperview()
@ -84,20 +84,6 @@ class AttachmentsManager {
imageView.addConstraint(getHeightConstraint(view: imageView, height: view.frame.width))
imageView.addConstraint(getWidthConstraint(view: imageView, width: halfWidth))
}
/*
let betweenConstraint = NSLayoutConstraint(
item: firstImageView,
attribute: NSLayoutConstraint.Attribute.trailing,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: secondImageView,
attribute: NSLayoutConstraint.Attribute.leading,
multiplier: 1,
constant: 4
)
view.addConstraint(betweenConstraint)
*/
case 3:
let primaryFilter = AspectScaledToFillSizeFilter(size: CGSize(width: halfWidth, height: view.frame.width))
let secondaryFilter = AspectScaledToFillSizeFilter(size: CGSize(width: halfWidth, height: halfWidth))

12
elpha-ios/AttachmentsView.swift

@ -1,12 +0,0 @@
//
// AttachmentsView.swift
// elpha-ios
//
// Created by Dwayne Harris on 10/6/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import UIKit
class AttachmentsView: UIView {
}

2
elpha-ios/AuthenticationManager.swift

@ -50,7 +50,7 @@ class AuthenticationManager {
}
}
func getMKClientForSelectedSession() -> Client? {
func mkClientForSelectedSession() -> Client? {
guard let session = selectedSession, let client = session.client else {
return nil
}

2
elpha-ios/Base.lproj/Main.storyboard

@ -827,7 +827,7 @@
<constraint firstAttribute="trailing" secondItem="dvJ-7r-JOr" secondAttribute="trailing" constant="8" id="u1Y-8l-2XB"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="k2r-TR-ccp" customClass="AttachmentsView" customModule="elpha_ios" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="k2r-TR-ccp">
<rect key="frame" x="0.0" y="384" width="414" height="1"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>

24
elpha-ios/MastodonDataManager.swift

@ -10,6 +10,16 @@ import CoreData
import Foundation
import MastodonKit
class UpsertResult<T> {
var model: T
var new: Bool = false
init(model: T, new: Bool) {
self.model = model
self.new = new
}
}
public class MastodonDataManager {
static func upsertAccount(_ remoteAccount: Account) -> AccountMO? {
func saveAccount(_ account: AccountMO) -> AccountMO? {
@ -158,8 +168,8 @@ public class MastodonDataManager {
}
}
static func upsertStatus(_ remoteStatus: Status) -> StatusMO? {
func saveStatus(_ status: StatusMO) -> StatusMO? {
static func upsertStatus(_ remoteStatus: Status) -> UpsertResult<StatusMO>? {
func saveStatus(_ status: StatusMO) -> StatusMO {
status.id = remoteStatus.id
status.uri = URL(string: remoteStatus.uri)
status.url = remoteStatus.url
@ -200,8 +210,8 @@ public class MastodonDataManager {
}
if let reblog = remoteStatus.reblog {
let savedReblog = upsertStatus(reblog)
status.reblog = savedReblog
let savedReblogResult = upsertStatus(reblog)
status.reblog = savedReblogResult?.model
}
return status
@ -214,9 +224,9 @@ public class MastodonDataManager {
do {
let results = try context.fetch(request)
if let status = results.first {
return saveStatus(status)
return UpsertResult(model: saveStatus(status), new: false)
} else {
return saveStatus(StatusMO(context: context))
return UpsertResult(model: saveStatus(StatusMO(context: context)), new: true)
}
} catch {
print("\(error)")
@ -258,7 +268,7 @@ public class MastodonDataManager {
return
}
guard let client = AuthenticationManager.shared.getMKClientForSelectedSession() else {
guard let client = AuthenticationManager.shared.mkClientForSelectedSession() else {
completion(nil, NSError())
return
}

35
elpha-ios/NewStatusesView.swift

@ -0,0 +1,35 @@
//
// NewStatusesView.swift
// elpha-ios
//
// Created by Dwayne Harris on 10/7/18.
// Copyright © 2018 Elpha. All rights reserved.
//
import UIKit
@IBDesignable class NewStatusesView: UIView {
@IBOutlet var contentView: UIView!
@IBOutlet var mainLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("NewStatusesView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
public func setCount(_ count: Int) {
mainLabel.text = "\(count) New Toots"
}
}

42
elpha-ios/NewStatusesView.xib

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NewStatusesView" customModule="elpha_ios" customModuleProvider="target">
<connections>
<outlet property="contentView" destination="iN0-l3-epB" id="COo-K8-7Wi"/>
<outlet property="mainLabel" destination="uLF-4h-Bxo" id="KHJ-Zg-Cre"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="375" height="180"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="New Toots" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uLF-4h-Bxo">
<rect key="frame" x="8" y="79.5" width="359" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.090196078430000007" green="0.047058823530000002" blue="0.28627450980000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="uLF-4h-Bxo" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="Ojm-wu-q8j"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="uLF-4h-Bxo" secondAttribute="trailing" constant="8" id="UuS-UO-ukK"/>
<constraint firstItem="uLF-4h-Bxo" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="hld-30-Yt0"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<point key="canvasLocation" x="-574" y="-232"/>
</view>
</objects>
</document>

2
elpha-ios/TimelineTableViewCell.swift

@ -32,6 +32,6 @@ class TimelineTableViewCell: UITableViewCell {
@IBOutlet var topLoadMoreView: UIView!
@IBOutlet var bottomDividerView: UIView!
@IBOutlet var bottomLoadMoreView: UIView!
@IBOutlet var attachmentsView: AttachmentsView!
@IBOutlet var attachmentsView: UIView!
@IBOutlet var attachmentsHeightConstraint: NSLayoutConstraint!
}

220
elpha-ios/TimelineTableViewController.swift

@ -35,14 +35,37 @@ class TimelineTableViewController: UITableViewController {
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 500
let cleanButtonItem = UIBarButtonItem(image: UIImage(named: "Refresh CCW"), style: .plain, target: self, action: #selector(clean))
let moreButtonItem = UIBarButtonItem(image: UIImage(named: "More"), style: .plain, target: self, action: #selector(more))
let composeButtonItem = UIBarButtonItem(image: UIImage(named: "Compose"), style: .plain, target: self, action: #selector(compose))
navigationItem.rightBarButtonItem = composeButtonItem
navigationItem.rightBarButtonItems = [composeButtonItem, moreButtonItem, cleanButtonItem]
navigationItem.title = "Home"
refreshControl?.addTarget(self, action: #selector(self.fetchTimelineWithDefaultRange), for: .valueChanged)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
fetchTimelineWithDefaultRange()
}
@objc func clean() {
guard let session = AuthenticationManager.shared.selectedSession, let timeline = session.selectedTimeline else {
return
}
timeline.mutableSetValue(forKey: "statuses").removeAllObjects()
timeline.mutableSetValue(forKey: "boundaries").removeAllObjects()
CoreDataManager.shared.saveContext()
self.tableView.reloadData()
}
@objc func more() {
}
@objc func compose() {
let alertController = UIAlertController(title: "Compose", message: "Toot", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
@ -66,58 +89,67 @@ class TimelineTableViewController: UITableViewController {
CoreDataManager.shared.saveContext()
}
func fetchStatuses(request: Request<[Status]>, forTimeline timeline: TimelineMO) {
if let client = AuthenticationManager.shared.getMKClientForSelectedSession() {
print("Running request \(request)")
client.run(request) { result in
switch result {
case .success(let remoteStatuses, _):
DispatchQueue.main.async {
let context = CoreDataManager.shared.getContext()
let statuses = remoteStatuses.compactMap { status in
return MastodonDataManager.upsertStatus(status)
}
for (index, status) in statuses.enumerated() {
if index == 0 {
let boundary = TimelineBoundaryMO(context: context)
boundary.statusID = status.id!
boundary.start = true
boundary.createdAt = Date()
timeline.mutableSetValue(forKey: "boundaries").add(boundary)
} else if index == statuses.count {
let boundary = TimelineBoundaryMO(context: context)
boundary.statusID = status.id!
boundary.start = false
boundary.createdAt = Date()
timeline.mutableSetValue(forKey: "boundaries").add(boundary)
} else {
let predicate = NSPredicate(format: "statusID != %@", status.id!)
timeline.mutableSetValue(forKey: "boundaries").filter(using: predicate)
}
status.addToTimelines(timeline)
func fetchStatuses(request: Request<[Status]>, forTimeline timeline: TimelineMO, completion: @escaping ([UpsertResult<StatusMO>], Error?) -> Void) {
guard let client = AuthenticationManager.shared.mkClientForSelectedSession() else {
completion([], nil)
return
}
print("Request \(request)")
client.run(request) { result in
switch result {
case .success(let remoteStatuses, _):
DispatchQueue.main.async {
let context = CoreDataManager.shared.getContext()
let statuses = remoteStatuses.compactMap { status in
return MastodonDataManager.upsertStatus(status)
}
for (index, statusResult) in statuses.enumerated() {
if index == 0 {
let boundary = TimelineBoundaryMO(context: context)
boundary.statusID = statusResult.model.id!
boundary.start = true
boundary.createdAt = Date()
timeline.mutableSetValue(forKey: "boundaries").add(boundary)
} else if index == statuses.count {
let boundary = TimelineBoundaryMO(context: context)
boundary.statusID = statusResult.model.id!
boundary.start = false
boundary.createdAt = Date()
timeline.mutableSetValue(forKey: "boundaries").add(boundary)
} else {
let predicate = NSPredicate(format: "statusID != %@", statusResult.model.id!)
timeline.mutableSetValue(forKey: "boundaries").filter(using: predicate)
}
// timeline.addToStatuses(NSSet(array: statuses))
CoreDataManager.shared.saveContext()
self.loading = false
self.tableView.reloadData()
statusResult.model.addToTimelines(timeline)
}
case .failure(let error):
print("\(error)")
// timeline.addToStatuses(NSSet(array: statuses))
CoreDataManager.shared.saveContext()
self.loading = false
completion(statuses, nil)
}
case .failure(let error):
completion([], error)
}
}
}
@objc func fetchTimelineWithDefaultRange() {
fetchTimeline(withRange: .limit(fetchLimit))
fetchTimeline(withRange: .limit(fetchLimit)) { error in
guard error == nil else {
return
}
}
}
func fetchTimeline(withRange requestRange: RequestRange) {
func fetchTimeline(withRange requestRange: RequestRange, completion: @escaping (Error?) -> Void) {
guard let session = AuthenticationManager.shared.selectedSession, let account = session.account else {
completion(nil)
return
}
@ -138,42 +170,48 @@ class TimelineTableViewController: UITableViewController {
}
}
/*
let request = NSFetchRequest<TimelineMO>(entityName: "Timeline")
request.predicate = NSPredicate(format: "name == %@", "Federated")
do {
let results = try CoreDataManager.shared.getContext().fetch(request)
session.selectedTimeline = results.first
CoreDataManager.shared.saveContext()
navigationItem.title = "Federated"
} catch {
print("\(error)")
guard let selectedTimeline = session.selectedTimeline else {
completion(nil)
return
}
*/
loading = true
if let selectedTimeline = session.selectedTimeline {
var request: Request<[Status]>
var request: Request<[Status]>
switch selectedTimeline.name {
case "Home":
request = Timelines.home(range: requestRange)
case "Local":
request = Timelines.public(local: true, range: requestRange)
case "Federated":
request = Timelines.public(local: false, range: requestRange)
case let tag:
request = Timelines.tag(tag!, range: requestRange)
}
fetchStatuses(request: request, forTimeline: selectedTimeline) { statuses, error in
guard error == nil else {
completion(error)
return
}
switch selectedTimeline.name {
case "Home":
request = Timelines.home(range: requestRange)
case "Local":
request = Timelines.public(local: true, range: requestRange)
case "Federated":
request = Timelines.public(local: false, range: requestRange)
case let tag:
request = Timelines.tag(tag!, range: requestRange)
let newStatuses = statuses.filter { $0.new }
DispatchQueue.main.async {
if let navigationController = self.navigationController as? TimelinesNavigationController,
let newStatusesView = navigationController.newStatusesView {
newStatusesView.isHidden = false
newStatusesView.setCount(newStatuses.count)
}
}
fetchStatuses(request: request, forTimeline: selectedTimeline)
completion(nil)
}
}
func getTimelineRequest() -> NSFetchRequest<TimelineMO>? {
}
extension TimelineTableViewController {
func timelineRequest() -> NSFetchRequest<TimelineMO>? {
guard let session = AuthenticationManager.shared.selectedSession, let selectedTimeline = session.selectedTimeline else {
return nil
}
@ -184,8 +222,8 @@ class TimelineTableViewController: UITableViewController {
return request
}
func getTimelineStatuses() -> [StatusMO]? {
guard let request = getTimelineRequest() else {
func timelineStatuses() -> [StatusMO]? {
guard let request = timelineRequest() else {
return []
}
@ -203,28 +241,32 @@ class TimelineTableViewController: UITableViewController {
}
}
func getTimelineStatusesCount() -> Int {
guard let statuses = getTimelineStatuses() else {
func timelineStatusesCount() -> Int {
guard let statuses = timelineStatuses() else {
return 0
}
return statuses.count
}
func getTimelineBoundaries() -> NSSet? {
guard let session = AuthenticationManager.shared.selectedSession, let timeline = session.selectedTimeline else {
return nil
func timelineBoundaries() -> NSSet {
guard let session = AuthenticationManager.shared.selectedSession,
let timeline = session.selectedTimeline,
let boundaries = timeline.boundaries else {
return NSSet()
}
return timeline.boundaries
return boundaries
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
extension TimelineTableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return getTimelineStatusesCount()
return timelineStatusesCount()
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
@ -255,9 +297,9 @@ class TimelineTableViewController: UITableViewController {
cell.attachmentsHeightConstraint.constant = cell.frame.width
cell.attachmentsView.isHidden = true
if let statuses = getTimelineStatuses() {
if let statuses = timelineStatuses() {
let status = statuses[indexPath.row]
let boundaries = getTimelineBoundaries() ?? NSSet()
let boundaries = timelineBoundaries()
let avatarFilter = AspectScaledToFillSizeWithRoundedCornersFilter(
size: CGSize(width: 40.0, height: 40.0),
@ -268,7 +310,8 @@ class TimelineTableViewController: UITableViewController {
if indexPath.row != 0 {
if let boundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", status.id!)).first as? TimelineBoundaryMO {
if boundary.start {
if let previousBoundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", statuses[indexPath.row - 1])).first as? TimelineBoundaryMO {
let previousStatus = statuses[indexPath.row - 1]
if let previousBoundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", previousStatus.id!)).first as? TimelineBoundaryMO {
if !previousBoundary.start {
cell.topDividerView.isHidden = true
cell.topLoadMoreView.isHidden = false
@ -276,12 +319,15 @@ class TimelineTableViewController: UITableViewController {
}
}
}
} else {
cell.topDividerView.isHidden = true
}
if indexPath.row < statuses.count - 1 {
if let boundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", status.id!)).first as? TimelineBoundaryMO {
if !boundary.start {
if let nextBoundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", statuses[indexPath.row + 1])).first as? TimelineBoundaryMO {
let nextStatus = statuses[indexPath.row + 1]
if let nextBoundary = boundaries.filtered(using: NSPredicate(format: "statusID = %@", nextStatus.id!)).first as? TimelineBoundaryMO {
if nextBoundary.start {
cell.bottomDividerView.isHidden = true
cell.bottomLoadMoreView.isHidden = false
@ -367,7 +413,15 @@ class TimelineTableViewController: UITableViewController {
if indexPath.row == statuses.count - 1 && !loading {
fetchTimeline(withRange: .max(id: status.id!, limit: fetchLimit))
fetchTimeline(withRange: .max(id: status.id!, limit: fetchLimit)) { error in
guard error == nil else {
return
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
return cell

32
elpha-ios/TimelinesNavigationController.swift

@ -9,5 +9,37 @@
import UIKit
class TimelinesNavigationController: UINavigationController {
public var newStatusesView: NewStatusesView? = nil
override func viewDidLoad() {
super.viewDidLoad()
let newStatusesView = NewStatusesView()
newStatusesView.translatesAutoresizingMaskIntoConstraints = false
let blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffect.Style.prominent))
blurEffectView.translatesAutoresizingMaskIntoConstraints = false
blurEffectView.contentView.addSubview(newStatusesView)
blurEffectView.layer.cornerRadius = 10
blurEffectView.layer.masksToBounds = true
NSLayoutConstraint.activate([
newStatusesView.leadingAnchor.constraint(equalTo: blurEffectView.leadingAnchor),
newStatusesView.trailingAnchor.constraint(equalTo: blurEffectView.trailingAnchor),
newStatusesView.topAnchor.constraint(equalTo: blurEffectView.topAnchor),
newStatusesView.bottomAnchor.constraint(equalTo: blurEffectView.bottomAnchor),
])
view.addSubview(blurEffectView)
NSLayoutConstraint.activate([
blurEffectView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
blurEffectView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
blurEffectView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
blurEffectView.heightAnchor.constraint(equalToConstant: 50),
])
self.newStatusesView = newStatusesView
newStatusesView.isHidden = true
}
}
Loading…
Cancel
Save