Conversation view
This commit is contained in:
parent
05c895db88
commit
0f1a13d2a7
|
@ -14,6 +14,8 @@
|
|||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
|
||||
D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */; };
|
||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; };
|
||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; };
|
||||
D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */; };
|
||||
D667E5E12134937B0057A976 /* StatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* StatusTableViewCell.xib */; };
|
||||
D667E5E3213499F70057A976 /* Profile.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E2213499F70057A976 /* Profile.storyboard */; };
|
||||
D667E5E721349D4C0057A976 /* ProfileTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5E621349D4C0057A976 /* ProfileTableViewController.swift */; };
|
||||
|
@ -21,6 +23,9 @@
|
|||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */; };
|
||||
D667E5EF2134C39F0057A976 /* StatusContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */; };
|
||||
D667E5F12134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */; };
|
||||
D667E5F32135BC260057A976 /* Conversation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D667E5F22135BC260057A976 /* Conversation.storyboard */; };
|
||||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationViewController.swift */; };
|
||||
D667E5F82135C3040057A976 /* Status+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Status+Equatable.swift */; };
|
||||
D6BED16F212663DA00F02DA0 /* SwiftSoup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; };
|
||||
D6BED170212663DA00F02DA0 /* SwiftSoup.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */; };
|
||||
|
@ -77,6 +82,8 @@
|
|||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
|
||||
D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = "<group>"; };
|
||||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = "<group>"; };
|
||||
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConversationMainStatusTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMainStatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D667E5E02134937B0057A976 /* StatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatusTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D667E5E2213499F70057A976 /* Profile.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Profile.storyboard; sourceTree = "<group>"; };
|
||||
D667E5E621349D4C0057A976 /* ProfileTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTableViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -84,6 +91,9 @@
|
|||
D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentLabel.swift; sourceTree = "<group>"; };
|
||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+StatusTableViewCellDelegate.swift"; sourceTree = "<group>"; };
|
||||
D667E5F22135BC260057A976 /* Conversation.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Conversation.storyboard; sourceTree = "<group>"; };
|
||||
D667E5F42135BCD50057A976 /* ConversationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationViewController.swift; sourceTree = "<group>"; };
|
||||
D667E5F72135C3040057A976 /* Status+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+Equatable.swift"; sourceTree = "<group>"; };
|
||||
D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSoup.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6D4DDCC212518A000E1C4BB /* Tusker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tusker.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -131,6 +141,15 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
D667E5F62135C2ED0057A976 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */,
|
||||
D667E5F72135C3040057A976 /* Status+Equatable.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6BED1722126661300F02DA0 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -138,9 +157,10 @@
|
|||
D667E5EE2134C39F0057A976 /* StatusContentLabel.swift */,
|
||||
D667E5E02134937B0057A976 /* StatusTableViewCell.xib */,
|
||||
D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */,
|
||||
D667E5F02134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift */,
|
||||
D667E5E821349EE50057A976 /* ProfileHeaderTableViewCell.xib */,
|
||||
D667E5EA21349EF80057A976 /* ProfileHeaderTableViewCell.swift */,
|
||||
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */,
|
||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -174,6 +194,7 @@
|
|||
04DACE89212CA6B7009840C4 /* Timeline.swift */,
|
||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||
04DACE8D212CC7CC009840C4 /* AvatarCache.swift */,
|
||||
D667E5F62135C2ED0057A976 /* Extensions */,
|
||||
D6F953F121251A2F00CF0F2B /* Controllers */,
|
||||
D6F953E9212519B800CF0F2B /* View Controllers */,
|
||||
D6BED1722126661300F02DA0 /* Views */,
|
||||
|
@ -210,6 +231,7 @@
|
|||
D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */,
|
||||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */,
|
||||
D667E5E621349D4C0057A976 /* ProfileTableViewController.swift */,
|
||||
D667E5F42135BCD50057A976 /* ConversationViewController.swift */,
|
||||
);
|
||||
path = "View Controllers";
|
||||
sourceTree = "<group>";
|
||||
|
@ -221,6 +243,7 @@
|
|||
D6F953ED21251A0700CF0F2B /* Timeline.storyboard */,
|
||||
D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */,
|
||||
D667E5E2213499F70057A976 /* Profile.storyboard */,
|
||||
D667E5F22135BC260057A976 /* Conversation.storyboard */,
|
||||
);
|
||||
path = Storyboards;
|
||||
sourceTree = "<group>";
|
||||
|
@ -338,10 +361,12 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D667E5F32135BC260057A976 /* Conversation.storyboard in Resources */,
|
||||
D667E5E921349EE50057A976 /* ProfileHeaderTableViewCell.xib in Resources */,
|
||||
D6D4DDDA212518A200E1C4BB /* LaunchScreen.storyboard in Resources */,
|
||||
D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */,
|
||||
D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */,
|
||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
||||
D6F953EE21251A0700CF0F2B /* Timeline.storyboard in Resources */,
|
||||
D6D4DDD5212518A000E1C4BB /* Main.storyboard in Resources */,
|
||||
D667E5E3213499F70057A976 /* Profile.storyboard in Resources */,
|
||||
|
@ -372,7 +397,9 @@
|
|||
files = (
|
||||
04DACE8A212CA6B7009840C4 /* Timeline.swift in Sources */,
|
||||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */,
|
||||
D667E5F52135BCD50057A976 /* ConversationViewController.swift in Sources */,
|
||||
D6F953F021251A2900CF0F2B /* MastodonController.swift in Sources */,
|
||||
D667E5F82135C3040057A976 /* Status+Equatable.swift in Sources */,
|
||||
04DACE8E212CC7CC009840C4 /* AvatarCache.swift in Sources */,
|
||||
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
|
||||
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
|
||||
|
@ -382,6 +409,7 @@
|
|||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
|
||||
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */,
|
||||
D667E5F12134D5050057A976 /* UIViewController+StatusTableViewCellDelegate.swift in Sources */,
|
||||
D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */,
|
||||
D667E5E721349D4C0057A976 /* ProfileTableViewController.swift in Sources */,
|
||||
D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Status+Equatable.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 8/28/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import MastodonKit
|
||||
|
||||
extension Status: Equatable {
|
||||
public static func ==(lhs: Status, rhs: Status) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
}
|
||||
}
|
|
@ -33,4 +33,12 @@ extension UIViewController: StatusTableViewCellDelegate {
|
|||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
func selected(status: Status) {
|
||||
guard let navigationController = navigationController else {
|
||||
fatalError("Can't show conversation VC when not in navigation controller")
|
||||
}
|
||||
let vc = ConversationViewController.create(for: status)
|
||||
navigationController.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.13.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="3Yl-E3-qsN">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.9"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Conversation View Controller-->
|
||||
<scene sceneID="rkM-By-3Qj">
|
||||
<objects>
|
||||
<viewController id="3Yl-E3-qsN" customClass="ConversationViewController" customModule="Tusker" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="yCk-Ig-NdG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="k5U-Kb-0kH">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="k5U-Kb-0kH" firstAttribute="leading" secondItem="mo7-1M-ylA" secondAttribute="leading" id="3Xe-vE-WWU"/>
|
||||
<constraint firstItem="k5U-Kb-0kH" firstAttribute="top" secondItem="mo7-1M-ylA" secondAttribute="top" id="X2A-0c-iZD"/>
|
||||
<constraint firstItem="mo7-1M-ylA" firstAttribute="bottom" secondItem="k5U-Kb-0kH" secondAttribute="bottom" id="bXb-L7-DfD"/>
|
||||
<constraint firstItem="k5U-Kb-0kH" firstAttribute="trailing" secondItem="mo7-1M-ylA" secondAttribute="trailing" id="c6s-ef-Dxs"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="mo7-1M-ylA"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="k5U-Kb-0kH" id="vAY-xd-xkg"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BtE-Vw-c42" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-358" y="16"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// ConversationTableViewController.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 8/28/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MastodonKit
|
||||
|
||||
class ConversationViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
static func create(for mainStatus: Status) -> ConversationViewController {
|
||||
guard let conversationController = UIStoryboard(name: "Conversation", bundle: nil).instantiateInitialViewController() as? ConversationViewController else { fatalError() }
|
||||
conversationController.mainStatus = mainStatus
|
||||
return conversationController
|
||||
}
|
||||
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
var mainStatus: Status!
|
||||
|
||||
var statuses: [Status] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
|
||||
tableView.register(UINib(nibName: "StatusTableViewCell", bundle: nil), forCellReuseIdentifier: "statusCell")
|
||||
tableView.register(UINib(nibName: "ConversationMainStatusTableViewCell", bundle: nil), forCellReuseIdentifier: "mainStatusCell")
|
||||
|
||||
statuses = [mainStatus]
|
||||
|
||||
let req = Statuses.context(id: mainStatus.id)
|
||||
MastodonController.shared.client.run(req) { result in
|
||||
guard case let .success(context, _) = result else { fatalError() }
|
||||
var statuses = self.getDirectParents(of: self.mainStatus, from: context.ancestors)
|
||||
statuses.append(self.mainStatus)
|
||||
statuses.append(contentsOf: context.descendants)
|
||||
self.statuses = statuses
|
||||
let indexPath = IndexPath(row: statuses.firstIndex(of: self.mainStatus)!, section: 0)
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getDirectParents(of status: Status, from statuses: [Status]) -> [Status] {
|
||||
var statuses = statuses
|
||||
var parents: [Status] = []
|
||||
var currentStatus: Status? = status
|
||||
while currentStatus != nil {
|
||||
guard let index = statuses.firstIndex(where: { $0.id == currentStatus!.inReplyToID }) else { break }
|
||||
let parent = statuses.remove(at: index)
|
||||
parents.insert(parent, at: 0)
|
||||
currentStatus = parent
|
||||
}
|
||||
return parents
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
// Get the new view controller using segue.destination.
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return statuses.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let status = statuses[indexPath.row]
|
||||
|
||||
if status == mainStatus {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "mainStatusCell", for: indexPath) as? ConversationMainStatusTableViewCell else { fatalError() }
|
||||
cell.selectionStyle = .none
|
||||
cell.updateUI(for: status)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
} else {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
cell.updateUI(for: status)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let status = statuses[indexPath.row]
|
||||
guard status != mainStatus else { return }
|
||||
guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
cell.didSelect()
|
||||
}
|
||||
|
||||
}
|
|
@ -85,13 +85,13 @@ class ProfileTableViewController: UITableViewController {
|
|||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as! ProfileHeaderTableViewCell
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as? ProfileHeaderTableViewCell else { fatalError() }
|
||||
cell.selectionStyle = .none
|
||||
cell.updateUI(for: account)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
case 1:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! StatusTableViewCell
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
let status = statuses[indexPath.row]
|
||||
cell.updateUI(for: status)
|
||||
cell.delegate = self
|
||||
|
@ -101,6 +101,12 @@ class ProfileTableViewController: UITableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
guard indexPath.section == 1 else { return }
|
||||
guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
cell.didSelect()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if indexPath.section == 1 && indexPath.row == statuses.count - 1 {
|
||||
guard let older = older else { return }
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import UIKit
|
||||
import MastodonKit
|
||||
import SafariServices
|
||||
|
||||
class TimelineTableViewController: UITableViewController {
|
||||
|
||||
|
@ -83,7 +82,7 @@ class TimelineTableViewController: UITableViewController {
|
|||
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! StatusTableViewCell
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
|
||||
let status = statuses[indexPath.row]
|
||||
|
||||
|
@ -94,6 +93,11 @@ class TimelineTableViewController: UITableViewController {
|
|||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
cell.didSelect()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if indexPath.row == statuses.count - 1 {
|
||||
guard let older = older else { return }
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// ConversationMainStatusTableViewCell.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 8/28/18.
|
||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MastodonKit
|
||||
|
||||
class ConversationMainStatusTableViewCell: UITableViewCell {
|
||||
|
||||
var delegate: StatusTableViewCellDelegate?
|
||||
|
||||
@IBOutlet weak var displayNameLabel: UILabel!
|
||||
@IBOutlet weak var usernameLabel: UILabel!
|
||||
@IBOutlet weak var contentLabel: StatusContentLabel!
|
||||
@IBOutlet weak var avatarImageView: UIImageView!
|
||||
|
||||
var status: Status!
|
||||
var account: Account!
|
||||
|
||||
var avatarURL: URL?
|
||||
|
||||
override func awakeFromNib() {
|
||||
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
displayNameLabel.isUserInteractionEnabled = true
|
||||
usernameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
usernameLabel.isUserInteractionEnabled = true
|
||||
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
avatarImageView.isUserInteractionEnabled = true
|
||||
avatarImageView.layer.cornerRadius = 5
|
||||
avatarImageView.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
func updateUI(for status: Status) {
|
||||
self.status = status
|
||||
|
||||
let account: Account
|
||||
if let reblog = status.reblog {
|
||||
account = reblog.account
|
||||
} else {
|
||||
account = status.account
|
||||
}
|
||||
self.account = account
|
||||
|
||||
displayNameLabel.text = account.displayName
|
||||
usernameLabel.text = "@\(account.acct)"
|
||||
avatarImageView.image = nil
|
||||
if let url = URL(string: account.avatar) {
|
||||
avatarURL = url
|
||||
AvatarCache.shared.get(url) { image in
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
self.avatarURL = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentLabel.status = status
|
||||
contentLabel.delegate = self
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
if let url = avatarURL {
|
||||
AvatarCache.shared.cancel(url)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func accountPressed() {
|
||||
delegate?.selected(account: account)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ConversationMainStatusTableViewCell: HTMLContentLabelDelegate {
|
||||
|
||||
func selected(mention: Mention) {
|
||||
delegate?.selected(mention: mention)
|
||||
}
|
||||
|
||||
func selected(tag: MastodonKit.Tag) {
|
||||
delegate?.selected(tag: tag)
|
||||
}
|
||||
|
||||
func selected(url: URL) {
|
||||
delegate?.selected(url: url)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.13.2" 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.9"/>
|
||||
<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"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ConversationMainStatusTableViewCell" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="mB9-HO-1vf">
|
||||
<rect key="frame" x="16" y="8" width="50" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="XPF-UL-68q"/>
|
||||
<constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d">
|
||||
<rect key="frame" x="74" y="8" width="285" height="29"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SWg-Ka-QyP">
|
||||
<rect key="frame" x="74" y="37.5" width="285" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TgY-hs-Klo" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="16" y="66" width="343" height="126"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="TgY-hs-Klo" secondAttribute="trailing" constant="16" id="E3q-sb-FJE"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="lZY-2e-17d" secondAttribute="trailing" constant="16" id="ESw-bn-lfh"/>
|
||||
<constraint firstItem="lZY-2e-17d" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Egk-Kr-F8T"/>
|
||||
<constraint firstItem="TgY-hs-Klo" firstAttribute="top" secondItem="mB9-HO-1vf" secondAttribute="bottom" constant="8" id="U7U-WR-0eG"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="SWg-Ka-QyP" secondAttribute="trailing" constant="16" id="YaX-Rf-6px"/>
|
||||
<constraint firstItem="TgY-hs-Klo" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="Zn0-1F-UT0"/>
|
||||
<constraint firstItem="mB9-HO-1vf" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="avn-ir-MvB"/>
|
||||
<constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="bAC-TW-LbN"/>
|
||||
<constraint firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="bqf-Es-GfP"/>
|
||||
<constraint firstItem="SWg-Ka-QyP" firstAttribute="bottom" secondItem="mB9-HO-1vf" secondAttribute="bottom" id="c2R-gl-ybl"/>
|
||||
<constraint firstItem="lZY-2e-17d" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="uWl-Wj-Zvr"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="TgY-hs-Klo" secondAttribute="bottom" constant="8" id="zlq-nS-Uff"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<connections>
|
||||
<outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/>
|
||||
<outlet property="contentLabel" destination="TgY-hs-Klo" id="SEi-B2-VQf"/>
|
||||
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
|
||||
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="40.799999999999997" y="-163.71814092953525"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import UIKit
|
||||
import MastodonKit
|
||||
import SwiftSoup
|
||||
|
||||
protocol StatusTableViewCellDelegate {
|
||||
|
||||
|
@ -20,6 +19,8 @@ protocol StatusTableViewCellDelegate {
|
|||
|
||||
func selected(url: URL)
|
||||
|
||||
func selected(status: Status)
|
||||
|
||||
}
|
||||
|
||||
class StatusTableViewCell: UITableViewCell {
|
||||
|
@ -36,12 +37,6 @@ class StatusTableViewCell: UITableViewCell {
|
|||
|
||||
var avatarURL: URL?
|
||||
|
||||
var layoutManager: NSLayoutManager!
|
||||
var textContainer: NSTextContainer!
|
||||
var textStorage: NSTextStorage!
|
||||
|
||||
var links: [NSRange: URL] = [:]
|
||||
|
||||
override func awakeFromNib() {
|
||||
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||
displayNameLabel.isUserInteractionEnabled = true
|
||||
|
@ -91,6 +86,10 @@ class StatusTableViewCell: UITableViewCell {
|
|||
delegate?.selected(account: account)
|
||||
}
|
||||
|
||||
func didSelect() {
|
||||
delegate?.selected(status: status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusTableViewCell: HTMLContentLabelDelegate {
|
||||
|
|
Loading…
Reference in New Issue