forked from zelo72/mastodon-ios
status content display
This commit is contained in:
parent
cb690ffa4e
commit
24ca4644de
|
@ -10,6 +10,7 @@
|
|||
<entity name="MastodonUser" representedClassName=".MastodonUser" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="String"/>
|
||||
<attribute name="avatarStatic" optional="YES" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="displayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
|
@ -32,7 +33,7 @@
|
|||
</entity>
|
||||
<elements>
|
||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="104"/>
|
||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="179"/>
|
||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="194"/>
|
||||
<element name="Toot" positionX="0" positionY="0" width="128" height="164"/>
|
||||
</elements>
|
||||
</model>
|
|
@ -18,6 +18,9 @@ final public class MastodonUser: NSManagedObject {
|
|||
@NSManaged public private(set) var acct: String
|
||||
@NSManaged public private(set) var username: String
|
||||
@NSManaged public private(set) var displayName: String?
|
||||
@NSManaged public private(set) var avatar: String
|
||||
@NSManaged public private(set) var avatarStatic: String
|
||||
|
||||
|
||||
@NSManaged public private(set) var createdAt: Date
|
||||
@NSManaged public private(set) var updatedAt: Date
|
||||
|
@ -42,6 +45,8 @@ extension MastodonUser {
|
|||
user.acct = property.acct
|
||||
user.username = property.username
|
||||
user.displayName = property.displayName
|
||||
user.avatar = property.avatar
|
||||
user.avatarStatic = property.avatarStatic
|
||||
|
||||
user.createdAt = property.createdAt
|
||||
user.updatedAt = property.networkDate
|
||||
|
@ -60,6 +65,8 @@ extension MastodonUser {
|
|||
public let acct: String
|
||||
public let username: String
|
||||
public let displayName: String?
|
||||
public let avatar: String
|
||||
public let avatarStatic: String
|
||||
|
||||
public let createdAt: Date
|
||||
public let networkDate: Date
|
||||
|
@ -70,6 +77,8 @@ extension MastodonUser {
|
|||
acct: String,
|
||||
username: String,
|
||||
displayName: String?,
|
||||
avatar:String,
|
||||
avatarStatic:String,
|
||||
createdAt: Date,
|
||||
networkDate: Date
|
||||
) {
|
||||
|
@ -81,6 +90,8 @@ extension MastodonUser {
|
|||
self.displayName = displayName.flatMap { displayName in
|
||||
return displayName.isEmpty ? nil : displayName
|
||||
}
|
||||
self.avatar = avatar
|
||||
self.avatarStatic = avatarStatic
|
||||
self.createdAt = createdAt
|
||||
self.networkDate = networkDate
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
2D76319F25C1521200929FB9 /* TimelineSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D76319E25C1521200929FB9 /* TimelineSection.swift */; };
|
||||
2D7631A825C1535600929FB9 /* TimelinePostTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */; };
|
||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7631B225C159F700929FB9 /* Item.swift */; };
|
||||
2D9BB87B25C3FEF200678AB6 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9BB87A25C3FEF200678AB6 /* String.swift */; };
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
|
||||
3533495136D843E85211E3E2 /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1B4523A7981F1044DE89C21 /* Pods_Mastodon_MastodonUITests.framework */; };
|
||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 5D526FE125BE9AC400460CB9 /* MastodonSDK */; };
|
||||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||
|
@ -129,6 +131,8 @@
|
|||
2D76319E25C1521200929FB9 /* TimelineSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSection.swift; sourceTree = "<group>"; };
|
||||
2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePostTableViewCell.swift; sourceTree = "<group>"; };
|
||||
2D7631B225C159F700929FB9 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
|
||||
2D9BB87A25C3FEF200678AB6 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = "<group>"; };
|
||||
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -532,6 +536,8 @@
|
|||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */,
|
||||
2D46975D25C2A54100CF4AA9 /* NSLayoutConstraint.swift */,
|
||||
2D46976325C2A71500CF4AA9 /* UIIamge.swift */,
|
||||
2DF123A625C3B0210020F248 /* ActiveLabel.swift */,
|
||||
2D9BB87A25C3FEF200678AB6 /* String.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
|
@ -664,6 +670,7 @@
|
|||
TargetAttributes = {
|
||||
DB427DD125BAA00100D1B89D = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
LastSwiftMigration = 1220;
|
||||
};
|
||||
DB427DE725BAA00100D1B89D = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
|
@ -879,9 +886,11 @@
|
|||
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
||||
2D9BB87B25C3FEF200678AB6 /* String.swift in Sources */,
|
||||
2D152A8C25C295CC009AA50C /* TimelinePostView.swift in Sources */,
|
||||
2D61335825C188A000CAE157 /* APIService+Persist+Timeline.swift in Sources */,
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
||||
2D76316B25C14D4C00929FB9 /* PublicTimelineViewModel.swift in Sources */,
|
||||
2D46975E25C2A54100CF4AA9 /* NSLayoutConstraint.swift in Sources */,
|
||||
2D46976425C2A71500CF4AA9 /* UIIamge.swift in Sources */,
|
||||
|
@ -1126,6 +1135,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
|
@ -1137,6 +1147,7 @@
|
|||
MARKETING_VERSION = 1.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.Mastodon;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
@ -1148,6 +1159,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
|
|
|
@ -47,7 +47,8 @@ extension TimelineSection {
|
|||
) {
|
||||
cell.timelinePostView.nameLabel.text = toot.author.displayName
|
||||
cell.timelinePostView.usernameLabel.text = toot.author.username
|
||||
|
||||
cell.timelinePostView.avatarImageView.af.setImage(withURL: URL(string: toot.author.avatar)!)
|
||||
cell.timelinePostView.activeTextLabel.config(content: toot.content)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ActiveLabel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import ActiveLabel
|
||||
|
||||
|
||||
extension ActiveLabel {
|
||||
|
||||
enum Style {
|
||||
case `default`
|
||||
case timelineHeaderView
|
||||
}
|
||||
|
||||
convenience init(style: Style) {
|
||||
self.init()
|
||||
|
||||
switch style {
|
||||
case .default:
|
||||
// urlMaximumLength = 30
|
||||
font = .preferredFont(forTextStyle: .body)
|
||||
textColor = UIColor.label.withAlphaComponent(0.8)
|
||||
case .timelineHeaderView:
|
||||
font = .preferredFont(forTextStyle: .footnote)
|
||||
textColor = .secondaryLabel
|
||||
}
|
||||
|
||||
numberOfLines = 0
|
||||
mentionColor = UIColor.yellow
|
||||
hashtagColor = UIColor.blue
|
||||
URLColor = UIColor.red
|
||||
text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ActiveLabel {
|
||||
func config(content:String) {
|
||||
let html = content.replacingOccurrences(of: "</p><p>", with: "<br /><br />").replacingOccurrences(of: "<p>", with: "").replacingOccurrences(of: "</p>", with: "")
|
||||
text = html.toPlainText()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// String.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by sxiaojian on 2021/1/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
extension String {
|
||||
public func pregReplace(pattern: String, with: String, options: NSRegularExpression.Options = []) -> String {
|
||||
// swiftlint:disable force_try
|
||||
let regex = try! NSRegularExpression(pattern: pattern, options: options)
|
||||
return regex.stringByReplacingMatches(in: self, options: [], range: NSRange(location: 0, length: nsLength), withTemplate: with)
|
||||
}
|
||||
}
|
||||
extension String {
|
||||
public var nsLength: Int {
|
||||
let string_NS = self as NSString
|
||||
return string_NS.length
|
||||
}
|
||||
}
|
||||
extension String {
|
||||
func toPlainText() -> String {
|
||||
return self.pregReplace(pattern: "<br.+?>", with: "\n")
|
||||
.replacingOccurrences(of: "</p><p>", with: "\n\n")
|
||||
.pregReplace(pattern: "<.+?>", with: "")
|
||||
.replacingOccurrences(of: "<", with: "<")
|
||||
.replacingOccurrences(of: ">", with: ">")
|
||||
.replacingOccurrences(of: "'", with: "'")
|
||||
.replacingOccurrences(of: """, with: "\"")
|
||||
.replacingOccurrences(of: "&", with: "&")
|
||||
}
|
||||
}
|
||||
extension String {
|
||||
func string(in nsrange: NSRange) -> String? {
|
||||
guard let range = Range(nsrange, in: self) else { return nil }
|
||||
return String(self[range])
|
||||
}
|
||||
}
|
|
@ -22,6 +22,6 @@ extension HomeViewController {
|
|||
|
||||
title = "Home"
|
||||
view.backgroundColor = .systemBackground
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import UIKit
|
||||
import AVKit
|
||||
import ActiveLabel
|
||||
|
||||
final class TimelinePostView: UIView {
|
||||
|
||||
|
@ -41,6 +42,7 @@ final class TimelinePostView: UIView {
|
|||
|
||||
let mainContainerStackView = UIStackView()
|
||||
|
||||
let activeTextLabel = ActiveLabel(style: .default)
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
@ -108,6 +110,15 @@ extension TimelinePostView {
|
|||
dateLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
dateLabel.setContentCompressionResistancePriority(.required - 2, for: .horizontal)
|
||||
|
||||
// main container: [text | image / video | quote | geo]
|
||||
tweetContainerStackView.addArrangedSubview(mainContainerStackView)
|
||||
mainContainerStackView.axis = .vertical
|
||||
mainContainerStackView.spacing = 8
|
||||
activeTextLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
mainContainerStackView.addArrangedSubview(activeTextLabel)
|
||||
|
||||
activeTextLabel.setContentCompressionResistancePriority(.required - 2, for: .vertical)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ extension APIService.Persist {
|
|||
return managedObjectContext.performChanges {
|
||||
let toots = response.value
|
||||
let _ = toots.map {
|
||||
let userProperty = MastodonUser.Property(id: $0.account.id, domain: domain, acct: $0.account.acct, username: $0.account.username, displayName: $0.account.displayName, createdAt: $0.createdAt, networkDate: $0.createdAt)
|
||||
let userProperty = MastodonUser.Property(id: $0.account.id, domain: domain, acct: $0.account.acct, username: $0.account.username, displayName: $0.account.displayName,avatar: $0.account.avatar,avatarStatic: $0.account.avatarStatic, createdAt: $0.createdAt, networkDate: $0.createdAt)
|
||||
let author = MastodonUser.insert(into: managedObjectContext, property: userProperty)
|
||||
let tootProperty = Toot.Property(id: $0.id, domain: domain, content: $0.content, createdAt: $0.createdAt, networkDate: $0.createdAt)
|
||||
Toot.insert(into: managedObjectContext, property: tootProperty, author: author)
|
||||
|
|
|
@ -34,9 +34,9 @@ extension Mastodon.Entity {
|
|||
public let displayName: String
|
||||
public let note: String
|
||||
public let avatar: String
|
||||
public let avatarStatic: String?
|
||||
public let avatarStatic: String
|
||||
public let header: String
|
||||
public let headerStatic: String?
|
||||
public let headerStatic: String
|
||||
public let locked: Bool
|
||||
public let emojis: [Emoji]?
|
||||
public let discoverable: Bool?
|
||||
|
|
2
Podfile
2
Podfile
|
@ -9,7 +9,7 @@ target 'Mastodon' do
|
|||
# misc
|
||||
pod 'SwiftGen', '~> 6.4.0'
|
||||
pod 'DateToolsSwift', '~> 5.0.0'
|
||||
|
||||
pod 'ActiveLabel', git: 'https://github.com/ReticentJohn/ActiveLabel.swift.git', branch: 'master'
|
||||
target 'MastodonTests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
|
|
15
Podfile.lock
15
Podfile.lock
|
@ -1,8 +1,10 @@
|
|||
PODS:
|
||||
- ActiveLabel (1.1.0)
|
||||
- DateToolsSwift (5.0.0)
|
||||
- SwiftGen (6.4.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- ActiveLabel (from `https://github.com/ReticentJohn/ActiveLabel.swift.git`, branch `master`)
|
||||
- DateToolsSwift (~> 5.0.0)
|
||||
- SwiftGen (~> 6.4.0)
|
||||
|
||||
|
@ -11,10 +13,21 @@ SPEC REPOS:
|
|||
- DateToolsSwift
|
||||
- SwiftGen
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
ActiveLabel:
|
||||
:branch: master
|
||||
:git: https://github.com/ReticentJohn/ActiveLabel.swift.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
ActiveLabel:
|
||||
:commit: 01dd31cbbd1b3fec33b0c024b011e6b932794eff
|
||||
:git: https://github.com/ReticentJohn/ActiveLabel.swift.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
ActiveLabel: 5e3f4de79a1952d4604b845a0610d4776e4b82b3
|
||||
DateToolsSwift: 4207ada6ad615d8dc076323d27037c94916dbfa6
|
||||
SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108
|
||||
|
||||
PODFILE CHECKSUM: 5a58ccfd113912468e008313e1c91ed51b7cba20
|
||||
PODFILE CHECKSUM: 7fd5233d3180e2f7f67c96a28abbc20c6eddac93
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
|
Loading…
Reference in New Issue