diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents index aae7f5927..a6208aaeb 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents @@ -10,6 +10,7 @@ + @@ -32,7 +33,7 @@ - + \ No newline at end of file diff --git a/CoreDataStack/Entity/MastodonUser.swift b/CoreDataStack/Entity/MastodonUser.swift index 09b9f1604..718a589a4 100644 --- a/CoreDataStack/Entity/MastodonUser.swift +++ b/CoreDataStack/Entity/MastodonUser.swift @@ -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 } diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 7e9ca0e89..bfbf71607 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -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 = ""; }; 2D7631A725C1535600929FB9 /* TimelinePostTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePostTableViewCell.swift; sourceTree = ""; }; 2D7631B225C159F700929FB9 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + 2D9BB87A25C3FEF200678AB6 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; + 2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; 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 = ""; @@ -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; diff --git a/Mastodon/Diffiable/Section/TimelineSection.swift b/Mastodon/Diffiable/Section/TimelineSection.swift index e9fc68be8..49345c177 100644 --- a/Mastodon/Diffiable/Section/TimelineSection.swift +++ b/Mastodon/Diffiable/Section/TimelineSection.swift @@ -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) } } diff --git a/Mastodon/Extension/ActiveLabel.swift b/Mastodon/Extension/ActiveLabel.swift new file mode 100644 index 000000000..8423e8b97 --- /dev/null +++ b/Mastodon/Extension/ActiveLabel.swift @@ -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: "

", with: "

").replacingOccurrences(of: "

", with: "").replacingOccurrences(of: "

", with: "") + text = html.toPlainText() + } +} + diff --git a/Mastodon/Extension/String.swift b/Mastodon/Extension/String.swift new file mode 100644 index 000000000..b583d3f69 --- /dev/null +++ b/Mastodon/Extension/String.swift @@ -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: "", with: "\n") + .replacingOccurrences(of: "

", 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]) + } +} diff --git a/Mastodon/Scene/HomeViewController.swift b/Mastodon/Scene/HomeViewController.swift index 6a533558d..db2340f1d 100644 --- a/Mastodon/Scene/HomeViewController.swift +++ b/Mastodon/Scene/HomeViewController.swift @@ -22,6 +22,6 @@ extension HomeViewController { title = "Home" view.backgroundColor = .systemBackground + } - } diff --git a/Mastodon/Scene/Share/View/Content/TimelinePostView.swift b/Mastodon/Scene/Share/View/Content/TimelinePostView.swift index 12ba1ccc5..44443361c 100644 --- a/Mastodon/Scene/Share/View/Content/TimelinePostView.swift +++ b/Mastodon/Scene/Share/View/Content/TimelinePostView.swift @@ -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) @@ -107,6 +109,15 @@ extension TimelinePostView { usernameLabel.setContentCompressionResistancePriority(.defaultHigh - 1, for: .horizontal) 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) } diff --git a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift index b43ee1010..c2d1602d5 100644 --- a/Mastodon/Service/Persist/APIService+Persist+Timeline.swift +++ b/Mastodon/Service/Persist/APIService+Persist+Timeline.swift @@ -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) diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift index f794dabb9..6f167730e 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Account.swift @@ -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? diff --git a/Podfile b/Podfile index 32659fa54..dee59e4d9 100644 --- a/Podfile +++ b/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 diff --git a/Podfile.lock b/Podfile.lock index da5a99e0e..94ee59e39 100644 --- a/Podfile.lock +++ b/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