Browse Source

Development

master
Dwayne Harris 6 years ago
parent
commit
d9e51dd6f9
  1. 2
      elpha-ios/Base.lproj/Main.storyboard
  2. 28
      elpha-ios/Date+TimeAgo.swift
  3. 13
      elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents
  4. 7
      elpha-ios/MastodonDataManager.swift
  5. 77
      elpha-ios/TimelineTableViewController.swift

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

@ -607,7 +607,7 @@
<constraint firstItem="WfV-kx-uwj" firstAttribute="leading" secondItem="oRN-Cg-Tuh" secondAttribute="leading" id="zTG-Wl-wrg"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u1N-eJ-oKk">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u1N-eJ-oKk">
<rect key="frame" x="344" y="11" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="0pw-Ep-hi5"/>

28
elpha-ios/Date+TimeAgo.swift

@ -20,58 +20,58 @@ extension Date {
if let year = components.year {
if (year >= 2) {
return "\(year) years"
return "\(year)y"
} else if (year >= 1) {
return "Last year"
return "1y"
}
}
if let month = components.month {
if (month >= 2) {
return "\(month) months"
return "\(month)m"
} else if (month >= 1) {
return "Last month"
return "1m"
}
}
if let weekOfMonth = components.weekOfMonth {
if (weekOfMonth >= 2) {
return "\(weekOfMonth) weeks"
return "\(weekOfMonth)w"
} else if (weekOfMonth >= 1) {
return "Last week"
return "1w"
}
}
if let day = components.day {
if (day >= 2) {
return "\(day) days"
return "\(day)d"
} else if (day >= 1) {
return "Yesterday"
return "1d"
}
}
if let hour = components.hour {
if (hour >= 2) {
return "\(hour) hours"
return "\(hour)h"
} else if (hour >= 1) {
return "An hour ago"
return "1h"
}
}
if let minute = components.minute {
if (minute >= 2) {
return "\(minute) mins"
return "\(minute)m"
} else if (minute >= 1) {
return "A minute ago"
return "1m"
}
}
if let second = components.second {
if (second >= 3) {
return "\(second) secs"
return "\(second)s"
}
}
return "Just now"
return "Now"
}
}

13
elpha-ios/Elpha.xcdatamodeld/Elpha.xcdatamodel/contents

@ -113,6 +113,7 @@
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="inReplyToAccountID" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="inReplyToID" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="pinned" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="reblogged" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="reblogsCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="sensitive" optional="YES" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
@ -135,9 +136,16 @@
<entity name="Timeline" representedClassName="TimelineMO" syncable="YES" codeGenerationType="class">
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="timelines" inverseEntity="Account" syncable="YES"/>
<relationship name="boundaries" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TimelineBoundary" inverseName="timeline" inverseEntity="TimelineBoundary" syncable="YES"/>
<relationship name="session" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Session" inverseName="selectedTimeline" inverseEntity="Session" syncable="YES"/>
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="timelines" inverseEntity="Status" syncable="YES"/>
</entity>
<entity name="TimelineBoundary" representedClassName="TimelineBoundaryMO" syncable="YES" codeGenerationType="class">
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="start" attributeType="Boolean" usesScalarValueType="YES" syncable="YES"/>
<attribute name="statusID" attributeType="String" syncable="YES"/>
<relationship name="timeline" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Timeline" inverseName="boundaries" inverseEntity="Timeline" syncable="YES"/>
</entity>
<elements>
<element name="Account" positionX="-669.49609375" positionY="52.046875" width="128" height="345"/>
<element name="App" positionX="-423" positionY="252" width="128" height="90"/>
@ -148,8 +156,9 @@
<element name="ISLanguage" positionX="-286.21875" positionY="512.6171875" width="128" height="75"/>
<element name="Mention" positionX="-441" positionY="234" width="128" height="120"/>
<element name="Session" positionX="-445.046875" positionY="277.31640625" width="128" height="150"/>
<element name="Status" positionX="-459" positionY="216" width="128" height="345"/>
<element name="Status" positionX="-459" positionY="216" width="128" height="360"/>
<element name="Tag" positionX="-432" positionY="243" width="128" height="90"/>
<element name="Timeline" positionX="-468" positionY="207" width="128" height="105"/>
<element name="Timeline" positionX="-468" positionY="207" width="128" height="120"/>
<element name="TimelineBoundary" positionX="-468" positionY="207" width="128" height="105"/>
</elements>
</model>

7
elpha-ios/MastodonDataManager.swift

@ -159,7 +159,7 @@ public class MastodonDataManager {
}
static func upsertStatus(remoteStatus: Status) -> StatusMO? {
func saveStatus(_ status: StatusMO) throws -> StatusMO? {
func saveStatus(_ status: StatusMO) -> StatusMO? {
status.id = remoteStatus.id
status.uri = URL(string: remoteStatus.uri)
status.url = remoteStatus.url
@ -173,6 +173,7 @@ public class MastodonDataManager {
status.reblogged = remoteStatus.reblogged ?? false
status.favourited = remoteStatus.favourited ?? false
status.sensitive = remoteStatus.sensitive ?? false
status.pinned = remoteStatus.pinned ?? false
status.spoilerText = remoteStatus.spoilerText
status.visibility = remoteStatus.visibility.rawValue
@ -208,9 +209,9 @@ public class MastodonDataManager {
do {
let results = try context.fetch(request)
if let status = results.first {
return try saveStatus(status)
return saveStatus(status)
} else {
return try saveStatus(StatusMO(context: context))
return saveStatus(StatusMO(context: context))
}
} catch {
print("\(error)")

77
elpha-ios/TimelineTableViewController.swift

@ -13,6 +13,8 @@ import MastodonKit
import UIKit
class TimelineTableViewController: UITableViewController {
let fetchLimit = 50
var loading: Bool = false {
didSet {
DispatchQueue.main.async {
@ -30,17 +32,8 @@ class TimelineTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
refreshControl?.addTarget(self, action: #selector(self.fetchTimeline), for: .valueChanged)
let statusesCount = getTimelineStatusesCount()
fetchTimeline()
if statusesCount == 0 {
self.tableView.reloadData()
} else {
print("More data to load...")
self.tableView.reloadData()
}
refreshControl?.addTarget(self, action: #selector(self.fetchTimelineWithDefaultRange), for: .valueChanged)
fetchTimelineWithDefaultRange()
}
func createDefaultTimelines(account: AccountMO) {
@ -62,16 +55,40 @@ class TimelineTableViewController: UITableViewController {
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, _):
let statuses = remoteStatuses.compactMap { status in
return MastodonDataManager.upsertStatus(remoteStatus: status)
DispatchQueue.main.async {
let context = CoreDataManager.shared.getContext()
let statuses = remoteStatuses.compactMap { status in
return MastodonDataManager.upsertStatus(remoteStatus: 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)
}
}
timeline.addToStatuses(NSSet(array: statuses))
CoreDataManager.shared.saveContext()
self.tableView.reloadData()
}
timeline.addToStatuses(NSSet(array: statuses))
CoreDataManager.shared.saveContext()
case .failure(let error):
print("\(error)")
}
@ -79,7 +96,11 @@ class TimelineTableViewController: UITableViewController {
}
}
@objc func fetchTimeline() {
@objc func fetchTimelineWithDefaultRange() {
fetchTimeline(withRange: .limit(fetchLimit))
}
func fetchTimeline(withRange requestRange: RequestRange) {
guard let session = AuthenticationManager.shared.selectedSession, let account = session.account else {
return
}
@ -107,16 +128,20 @@ class TimelineTableViewController: UITableViewController {
}
if let selectedTimeline = session.selectedTimeline {
var request: Request<[Status]>
switch selectedTimeline.name {
case "Home":
fetchStatuses(request: Timelines.home(), forTimeline: selectedTimeline)
request = Timelines.home(range: requestRange)
case "Local":
fetchStatuses(request: Timelines.public(local: true), forTimeline: selectedTimeline)
request = Timelines.public(local: true, range: requestRange)
case "Federated":
fetchStatuses(request: Timelines.public(local: false), forTimeline: selectedTimeline)
default:
print("Tags not implemented")
request = Timelines.public(local: false, range: requestRange)
case let tag:
request = Timelines.tag(tag!, range: requestRange)
}
fetchStatuses(request: request, forTimeline: selectedTimeline)
}
}
@ -187,7 +212,7 @@ class TimelineTableViewController: UITableViewController {
if let content = status.content {
do {
let styledContent = "<style>html * { font-size: 16px; color: #170c49; font-family: -apple-system }</style> \(content)"
let styledContent = "<style>html * { font-size: 15px; color: #170c49; font-family: -apple-system }</style> \(content)"
let attributedText = try NSAttributedString(
data: styledContent.data(using: String.Encoding.unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html],
@ -202,6 +227,10 @@ class TimelineTableViewController: UITableViewController {
cell.timestampLabel.text = status.createdAt!.timeAgo()
if indexPath.row == statuses.count - 1 && !loading {
fetchTimeline(withRange: .max(id: status.id!, limit: fetchLimit))
}
return cell
}

Loading…
Cancel
Save