Show custom emojis in display names (where possible)

This commit is contained in:
Shadowfacts 2020-03-01 19:40:32 -05:00
parent 244659c262
commit 53707593a6
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
25 changed files with 213 additions and 86 deletions

View File

@ -163,6 +163,8 @@
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; }; D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; };
D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; }; D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; };
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; }; D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; };
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */; };
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9F240C8384002843CE /* EmojiLabel.swift */; };
D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; }; D6A3BC7723218E1300FD64D5 /* TimelineSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */; };
D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */; }; D6A3BC7923218E9200FD64D5 /* NotificationGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */; };
D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */; }; D6A3BC7C232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */; };
@ -443,6 +445,8 @@
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; }; D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; };
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = "<group>"; }; D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = "<group>"; };
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; }; D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; };
D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+Emoji.swift"; sourceTree = "<group>"; };
D6969E9F240C8384002843CE /* EmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLabel.swift; sourceTree = "<group>"; };
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; }; D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSegment.swift; sourceTree = "<group>"; };
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; }; D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationGroup.swift; sourceTree = "<group>"; };
D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNotificationGroupTableViewCell.swift; sourceTree = "<group>"; }; D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNotificationGroupTableViewCell.swift; sourceTree = "<group>"; };
@ -974,6 +978,7 @@
D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */, D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */,
D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */, D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */,
D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */, D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */,
D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1113,6 +1118,7 @@
D620483323D3801D008A63EF /* LinkTextView.swift */, D620483323D3801D008A63EF /* LinkTextView.swift */,
D620483523D38075008A63EF /* ContentTextView.swift */, D620483523D38075008A63EF /* ContentTextView.swift */,
D620483723D38190008A63EF /* StatusContentTextView.swift */, D620483723D38190008A63EF /* StatusContentTextView.swift */,
D6969E9F240C8384002843CE /* EmojiLabel.swift */,
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */, D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
04ED00B021481ED800567C53 /* SteppedProgressView.swift */, 04ED00B021481ED800567C53 /* SteppedProgressView.swift */,
D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */, D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */,
@ -1589,6 +1595,7 @@
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */, 04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */,
D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */, D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */,
D6BC9DB1232C61BC002CA326 /* NotificationsPageViewController.swift in Sources */, D6BC9DB1232C61BC002CA326 /* NotificationsPageViewController.swift in Sources */,
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */,
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */, D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
0454DDAF22B462EF00B8BB8E /* GalleryExpandAnimationController.swift in Sources */, 0454DDAF22B462EF00B8BB8E /* GalleryExpandAnimationController.swift in Sources */,
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */, D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
@ -1661,6 +1668,7 @@
D627943E23A564D400D38C68 /* ExploreViewController.swift in Sources */, D627943E23A564D400D38C68 /* ExploreViewController.swift in Sources */,
D6B053AB23BD2F1400A066FA /* AssetCollectionViewCell.swift in Sources */, D6B053AB23BD2F1400A066FA /* AssetCollectionViewCell.swift in Sources */,
D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */, D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */,
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */,
D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */, D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */,
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */, D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */, D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */,

View File

@ -11,16 +11,22 @@ import Pachyderm
extension Account { extension Account {
var realDisplayName: String { var displayOrUserName: String {
if displayName.isEmpty { if displayName.isEmpty {
return username return username
} else if Preferences.shared.hideCustomEmojiInUsernames {
return stripCustomEmoji(from: displayName)
} else { } else {
return displayName return displayName
} }
} }
var displayNameWithoutCustomEmoji: String {
if displayName.isEmpty {
return username
} else {
return stripCustomEmoji(from: displayName)
}
}
private static let customEmojiRegex = try! NSRegularExpression(pattern: ":[a-zA-Z0-9_]+:", options: []) private static let customEmojiRegex = try! NSRegularExpression(pattern: ":[a-zA-Z0-9_]+:", options: [])
private func stripCustomEmoji(from string: String) -> String { private func stripCustomEmoji(from string: String) -> String {

View File

@ -0,0 +1,29 @@
//
// NSTextAttachment+Emoji.swift
// Tusker
//
// Created by Shadowfacts on 3/1/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
extension NSTextAttachment {
// Based on https://github.com/ReticentJohn/Amaroq/blob/7c5b7088eb9fd1611dcb0f47d43bf8df093e142c/DireFloof/InlineImageHelpers.m
convenience init(emojiImage image: UIImage, in font: UIFont, with textColor: UIColor = .label) {
let adjustedCapHeight = font.capHeight - 1
var imageSizeMatchingFontSize = CGSize(width: image.size.width * (adjustedCapHeight / image.size.height), height: adjustedCapHeight)
let defaultScale: CGFloat = 1.4
imageSizeMatchingFontSize = CGSize(width: imageSizeMatchingFontSize.width * defaultScale, height: imageSizeMatchingFontSize.height * defaultScale)
UIGraphicsBeginImageContextWithOptions(imageSizeMatchingFontSize, false, 0.0)
textColor.set()
image.draw(in: CGRect(origin: .zero, size: imageSizeMatchingFontSize))
let attachmentImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init()
self.image = attachmentImage
}
}

View File

@ -203,7 +203,7 @@ class ComposeViewController: UIViewController {
replyAvatarImageViewTopConstraint!.isActive = true replyAvatarImageViewTopConstraint!.isActive = true
inReplyToContainer.isHidden = false inReplyToContainer.isHidden = false
inReplyToLabel.text = "In reply to \(inReplyTo.account.realDisplayName)" inReplyToLabel.text = "In reply to \(inReplyTo.account.displayOrUserName)"
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {

View File

@ -129,7 +129,7 @@ class ProfileTableViewController: EnhancedTableViewController {
@objc func updateUIForPreferences() { @objc func updateUIForPreferences() {
guard let accountID = accountID, let account = mastodonController.cache.account(for: accountID) else { return } guard let accountID = accountID, let account = mastodonController.cache.account(for: accountID) else { return }
navigationItem.title = account.realDisplayName navigationItem.title = account.displayOrUserName
} }
func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) { func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) {

View File

@ -37,8 +37,8 @@ class UserActivityManager {
activity.isEligibleForPrediction = true activity.isEligibleForPrediction = true
if let mentioning = mentioning { if let mentioning = mentioning {
activity.userInfo = ["mentioning": mentioning.acct] activity.userInfo = ["mentioning": mentioning.acct]
activity.title = "Send a message to \(mentioning.realDisplayName)" activity.title = "Send a message to \(mentioning.displayOrUserName)"
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.realDisplayName)" activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayOrUserName)"
} else { } else {
activity.userInfo = [:] activity.userInfo = [:]
activity.title = "New Post" activity.title = "New Post"

View File

@ -14,7 +14,7 @@ class AccountTableViewCell: UITableViewCell {
var mastodonController: MastodonController! { delegate?.apiController } var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: EmojiLabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
var accountID: String! var accountID: String!
@ -35,7 +35,7 @@ class AccountTableViewCell: UITableViewCell {
guard let account = mastodonController.cache.account(for: accountID) else { guard let account = mastodonController.cache.account(for: accountID) else {
fatalError("Missing cached account \(accountID!)") fatalError("Missing cached account \(accountID!)")
} }
displayNameLabel.text = account.realDisplayName displayNameLabel.updateForAccountDisplayName(account: account)
} }
func updateUI(accountID: String) { func updateUI(accountID: String) {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -26,7 +26,7 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Iif-9m-vM5">
<rect key="frame" x="74" y="11" width="230" height="44"/> <rect key="frame" x="74" y="11" width="230" height="44"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fhc-bZ-lkB" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="230" height="26"/> <rect key="frame" x="0.0" y="0.0" width="230" height="26"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>

View File

@ -12,7 +12,7 @@ import Pachyderm
class LargeAccountDetailView: UIView { class LargeAccountDetailView: UIView {
var avatarImageView = UIImageView() var avatarImageView = UIImageView()
var displayNameLabel = UILabel() var displayNameLabel = EmojiLabel()
var usernameLabel = UILabel() var usernameLabel = UILabel()
var avatarRequest: ImageCache.Request? var avatarRequest: ImageCache.Request?
@ -66,7 +66,7 @@ class LargeAccountDetailView: UIView {
} }
func update(account: Account) { func update(account: Account) {
displayNameLabel.text = account.realDisplayName displayNameLabel.updateForAccountDisplayName(account: account)
usernameLabel.text = "@\(account.acct)" usernameLabel.text = "@\(account.acct)"
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in

View File

@ -14,7 +14,7 @@ class ComposeStatusReplyView: UIView {
weak var mastodonController: MastodonController? weak var mastodonController: MastodonController?
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: EmojiLabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var statusContentTextView: StatusContentTextView! @IBOutlet weak var statusContentTextView: StatusContentTextView!
@ -40,7 +40,7 @@ class ComposeStatusReplyView: UIView {
} }
func updateUI(for status: Status) { func updateUI(for status: Status) {
displayNameLabel.text = status.account.realDisplayName displayNameLabel.updateForAccountDisplayName(account: status.account)
usernameLabel.text = "@\(status.account.acct)" usernameLabel.text = "@\(status.account.acct)"
statusContentTextView.overrideMastodonController = mastodonController statusContentTextView.overrideMastodonController = mastodonController
statusContentTextView.statusID = status.id statusContentTextView.statusID = status.id

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -23,7 +23,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2cE-sS-Uut"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2cE-sS-Uut">
<rect key="frame" x="66" y="8" width="301" height="651"/> <rect key="frame" x="66" y="8" width="301" height="651"/>
<subviews> <subviews>
<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="Sdv-dB-Plm"> <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="Sdv-dB-Plm" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>

View File

@ -69,7 +69,7 @@ class ContentTextView: LinkTextView {
continue continue
} }
let attachment = self.createEmojiTextAttachment(image: emojiImage, index: match.range.location) let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.font!, with: self.textColor ?? .label)
let attachmentStr = NSAttributedString(attachment: attachment) let attachmentStr = NSAttributedString(attachment: attachment)
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr) mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
} }
@ -79,28 +79,6 @@ class ContentTextView: LinkTextView {
self.setNeedsDisplay() self.setNeedsDisplay()
} }
} }
// Based on https://github.com/ReticentJohn/Amaroq/blob/7c5b7088eb9fd1611dcb0f47d43bf8df093e142c/DireFloof/InlineImageHelpers.m
private func createEmojiTextAttachment(image: UIImage, index: Int) -> NSTextAttachment {
let font = self.font!
let adjustedCapHeight = font.capHeight - 1
var imageSizeMatchingFontSize = CGSize(width: image.size.width * (adjustedCapHeight / image.size.height), height: adjustedCapHeight)
let defaultScale: CGFloat = 1.4
imageSizeMatchingFontSize = CGSize(width: imageSizeMatchingFontSize.width * defaultScale, height: imageSizeMatchingFontSize.height * defaultScale)
let textColor = self.textColor ?? UIColor.label
UIGraphicsBeginImageContextWithOptions(imageSizeMatchingFontSize, false, 0.0)
textColor.set()
image.draw(in: CGRect(origin: .zero, size: imageSizeMatchingFontSize))
let attachmentImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let attachment = NSTextAttachment()
attachment.image = attachmentImage
return attachment
}
// MARK: - HTML Parsing // MARK: - HTML Parsing
func setTextFromHtml(_ html: String) { func setTextFromHtml(_ html: String) {

View File

@ -0,0 +1,94 @@
//
// EmojiLabel.swift
// Tusker
//
// Created by Shadowfacts on 3/1/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
class EmojiLabel: UILabel {
private var emojiIdentifier: String?
private var emojiRequests: [ImageCache.Request] = []
func setEmojis(_ emojis: [Emoji], identifier: String) {
guard emojis.count > 0, self.emojiIdentifier != identifier, let attributedText = attributedText else { return }
self.emojiIdentifier = identifier
emojiRequests.forEach { $0.cancel() }
emojiRequests = []
let matches = emojiRegex.matches(in: attributedText.string, options: [], range: attributedText.fullRange)
let emojiImages = CachedDictionary<UIImage>(name: "EmojiLabel Emoji Images")
let group = DispatchGroup()
for emoji in emojis {
// only make requests for emojis that are present in the text to avoid making unnecessary network requests
guard matches.contains(where: { (match) in
let matchShortcode = (attributedText.string as NSString).substring(with: match.range(at: 1))
return emoji.shortcode == matchShortcode
}) else {
continue
}
group.enter()
let request = ImageCache.emojis.get(emoji.url) { (data) in
defer { group.leave() }
guard let data = data, let image = UIImage(data: data) else {
return
}
emojiImages[emoji.shortcode] = image
}
if let request = request {
emojiRequests.append(request)
}
}
group.notify(queue: .main) { [weak self] in
// if e.g. the account changes before all emojis are loaded, don't bother trying to set them
guard let self = self, self.emojiIdentifier == identifier else { return }
let mutAttrString = NSMutableAttributedString(attributedString: attributedText)
// replaces the emojis starting from the end of the string as to not alter the indicies of preceeding emojis
for match in matches.reversed() {
let shortcode = (mutAttrString.string as NSString).substring(with: match.range(at: 1))
guard let emojiImage = emojiImages[shortcode] else {
continue
}
let attachment = NSTextAttachment(emojiImage: emojiImage, in: self.font, with: self.textColor)
let attachmentStr = NSAttributedString(attachment: attachment)
mutAttrString.replaceCharacters(in: match.range, with: attachmentStr)
}
self.attributedText = mutAttrString
self.setNeedsLayout()
self.setNeedsDisplay()
}
}
func removeEmojis() {
emojiIdentifier = nil
emojiRequests.forEach { $0.cancel() }
emojiRequests = []
}
}
extension EmojiLabel {
func updateForAccountDisplayName(account: Account) {
if Preferences.shared.hideCustomEmojiInUsernames {
self.text = account.displayNameWithoutCustomEmoji
self.removeEmojis()
} else {
self.text = account.displayOrUserName
self.setEmojis(account.emojis, identifier: account.id)
}
}
}

View File

@ -140,11 +140,11 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
// todo: figure out how to localize this // todo: figure out how to localize this
switch people.count { switch people.count {
case 1: case 1:
peopleStr = people.first!.realDisplayName peopleStr = people.first!.displayOrUserName
case 2: case 2:
peopleStr = people.first!.realDisplayName + " and " + people.last!.realDisplayName peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
default: default:
peopleStr = people.dropLast().map { $0.realDisplayName }.joined(separator: ", ") + ", and " + people.last!.realDisplayName peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName
} }
actionLabel.text = "\(verb) by \(peopleStr)" actionLabel.text = "\(verb) by \(peopleStr)"
} }

View File

@ -76,11 +76,11 @@ class FollowNotificationGroupTableViewCell: UITableViewCell {
let peopleStr: String let peopleStr: String
switch people.count { switch people.count {
case 1: case 1:
peopleStr = people.first!.realDisplayName peopleStr = people.first!.displayOrUserName
case 2: case 2:
peopleStr = people.first!.realDisplayName + " and " + people.last!.realDisplayName peopleStr = people.first!.displayOrUserName + " and " + people.last!.displayOrUserName
default: default:
peopleStr = people.dropLast().map { $0.realDisplayName }.joined(separator: ", ") + ", and " + people.last!.realDisplayName peopleStr = people.dropLast().map { $0.displayOrUserName }.joined(separator: ", ") + ", and " + people.last!.displayOrUserName
} }
actionLabel.text = "Followed by \(peopleStr)" actionLabel.text = "Followed by \(peopleStr)"

View File

@ -17,7 +17,7 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
@IBOutlet weak var stackView: UIStackView! @IBOutlet weak var stackView: UIStackView!
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var timestampLabel: UILabel! @IBOutlet weak var timestampLabel: UILabel!
@IBOutlet weak var actionLabel: UILabel! @IBOutlet weak var actionLabel: EmojiLabel!
@IBOutlet weak var actionButtonsStackView: UIStackView! @IBOutlet weak var actionButtonsStackView: UIStackView!
@IBOutlet weak var acceptButton: UIButton! @IBOutlet weak var acceptButton: UIButton!
@IBOutlet weak var rejectButton: UIButton! @IBOutlet weak var rejectButton: UIButton!
@ -53,7 +53,13 @@ class FollowRequestNotificationTableViewCell: UITableViewCell {
func updateUI(account: Account) { func updateUI(account: Account) {
self.account = account self.account = account
actionLabel.text = "Request to follow from \(account.realDisplayName)" if Preferences.shared.hideCustomEmojiInUsernames {
actionLabel.text = "Request to follow from \(account.displayNameWithoutCustomEmoji)"
actionLabel.removeEmojis()
} else {
actionLabel.text = "Request to follow from \(account.displayOrUserName)"
actionLabel.setEmojis(account.emojis, identifier: account.id)
}
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (data) in
guard let self = self, self.account == account, let data = data, let image = UIImage(data: data) else { return } guard let self = self, self.account == account, let data = data, let image = UIImage(data: data) else { return }
self.avatarRequest = nil self.avatarRequest = nil

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -40,7 +40,7 @@
</label> </label>
</subviews> </subviews>
</stackView> </stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Request to follow by Person 1" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aM6-C6-9QH"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Request to follow by Person 1" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aM6-C6-9QH" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="34" width="230" height="20.5"/> <rect key="frame" x="0.0" y="34" width="230" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
@ -102,8 +102,8 @@
</tableViewCell> </tableViewCell>
</objects> </objects>
<resources> <resources>
<image name="checkmark.circle.fill" catalog="system" width="64" height="60"/> <image name="checkmark.circle.fill" catalog="system" width="128" height="121"/>
<image name="person.fill" catalog="system" width="64" height="60"/> <image name="person.fill" catalog="system" width="128" height="120"/>
<image name="xmark.circle.fill" catalog="system" width="64" height="60"/> <image name="xmark.circle.fill" catalog="system" width="128" height="121"/>
</resources> </resources>
</document> </document>

View File

@ -22,7 +22,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
@IBOutlet weak var headerImageView: UIImageView! @IBOutlet weak var headerImageView: UIImageView!
@IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarContainerView: UIView!
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: EmojiLabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var followsYouLabel: UILabel! @IBOutlet weak var followsYouLabel: UILabel!
@IBOutlet weak var noteTextView: StatusContentTextView! @IBOutlet weak var noteTextView: StatusContentTextView!
@ -136,7 +136,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView) avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
displayNameLabel.text = account.realDisplayName displayNameLabel.updateForAccountDisplayName(account: account)
} }
override func prepareForReuse() { override func prepareForReuse() {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -45,7 +45,7 @@
<constraint firstItem="tH8-sR-DHC" firstAttribute="centerY" secondItem="KyB-ey-l11" secondAttribute="centerY" id="nYu-RE-MfA"/> <constraint firstItem="tH8-sR-DHC" firstAttribute="centerY" secondItem="KyB-ey-l11" secondAttribute="centerY" id="nYu-RE-MfA"/>
</constraints> </constraints>
</view> </view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="LjK-72-Bez"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="LjK-72-Bez" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="144" y="158" width="215" height="24"/> <rect key="frame" x="144" y="158" width="215" height="24"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
<nil key="textColor"/> <nil key="textColor"/>
@ -173,6 +173,6 @@
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="ellipsis" catalog="system" width="64" height="18"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
</resources> </resources>
</document> </document>

View File

@ -25,7 +25,7 @@ class BaseStatusTableViewCell: UITableViewCell {
var mastodonController: MastodonController! { overrideMastodonController ?? delegate?.apiController } var mastodonController: MastodonController! { overrideMastodonController ?? delegate?.apiController }
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: EmojiLabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var contentWarningLabel: UILabel! @IBOutlet weak var contentWarningLabel: UILabel!
@IBOutlet weak var collapseButton: UIButton! @IBOutlet weak var collapseButton: UIButton!
@ -190,7 +190,7 @@ class BaseStatusTableViewCell: UITableViewCell {
@objc func updateUIForPreferences() { @objc func updateUIForPreferences() {
guard let account = mastodonController.cache.account(for: accountID) else { return } guard let account = mastodonController.cache.account(for: accountID) else { return }
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
displayNameLabel.text = account.realDisplayName displayNameLabel.updateForAccountDisplayName(account: account)
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.cache.status(for: statusID)?.sensitive ?? false) attachmentsView.contentHidden = Preferences.shared.blurAllMedia || (mastodonController.cache.status(for: statusID)?.sensitive ?? false)
} }

View File

@ -60,7 +60,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
override func updateUI(account: Account) { override func updateUI(account: Account) {
super.updateUI(account: account) super.updateUI(account: account)
profileAccessibilityElement.accessibilityLabel = account.realDisplayName profileAccessibilityElement.accessibilityLabel = account.displayNameWithoutCustomEmoji
} }
@objc override func updateUIForPreferences() { @objc override func updateUIForPreferences() {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -26,7 +26,7 @@
<constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/> <constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/>
</constraints> </constraints>
</imageView> </imageView>
<label opaque="NO" contentMode="left" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d"> <label opaque="NO" contentMode="left" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="58" y="0.0" width="277" height="29"/> <rect key="frame" x="58" y="0.0" width="277" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<nil key="textColor"/> <nil key="textColor"/>
@ -224,10 +224,10 @@
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
<image name="chevron.down" catalog="system" width="64" height="36"/> <image name="chevron.down" catalog="system" width="128" height="72"/>
<image name="ellipsis" catalog="system" width="64" height="18"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="repeat" catalog="system" width="64" height="48"/> <image name="repeat" catalog="system" width="128" height="99"/>
<image name="star.fill" catalog="system" width="64" height="58"/> <image name="star.fill" catalog="system" width="128" height="116"/>
</resources> </resources>
</document> </document>

View File

@ -19,7 +19,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
return formatter return formatter
}() }()
@IBOutlet weak var reblogLabel: UILabel! @IBOutlet weak var reblogLabel: EmojiLabel!
@IBOutlet weak var timestampLabel: UILabel! @IBOutlet weak var timestampLabel: UILabel!
@IBOutlet weak var pinImageView: UIImageView! @IBOutlet weak var pinImageView: UIImageView!
@ -91,7 +91,13 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
} }
private func updateRebloggerLabel(reblogger: Account) { private func updateRebloggerLabel(reblogger: Account) {
reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)" if Preferences.shared.hideCustomEmojiInUsernames {
reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)"
reblogLabel.removeEmojis()
} else {
reblogLabel.text = "Reblogged by \(reblogger.displayOrUserName)"
reblogLabel.setEmojis(reblogger.emojis, identifier: reblogger.id)
}
} }
func updateTimestamp() { func updateTimestamp() {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16092.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16082.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
@ -16,7 +16,7 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c">
<rect key="frame" x="16" y="8" width="343" height="224"/> <rect key="frame" x="16" y="8" width="343" height="224"/>
<subviews> <subviews>
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ"> <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="163.5" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="163.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
@ -42,7 +42,7 @@
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Sm-P0-ySf"> <stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Sm-P0-ySf">
<rect key="frame" x="0.0" y="0.0" width="277" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="277" height="20.5"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" horizontalCompressionResistancePriority="749" verticalCompressionResistancePriority="752" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" horizontalCompressionResistancePriority="749" verticalCompressionResistancePriority="752" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/>
<accessibility key="accessibilityConfiguration"> <accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" notEnabled="YES"/> <accessibilityTraits key="traits" staticText="YES" notEnabled="YES"/>
@ -206,11 +206,11 @@
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
<image name="chevron.down" catalog="system" width="64" height="36"/> <image name="chevron.down" catalog="system" width="128" height="72"/>
<image name="ellipsis" catalog="system" width="64" height="18"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="pin.fill" catalog="system" width="58" height="64"/> <image name="pin.fill" catalog="system" width="119" height="128"/>
<image name="repeat" catalog="system" width="64" height="48"/> <image name="repeat" catalog="system" width="128" height="99"/>
<image name="star.fill" catalog="system" width="64" height="58"/> <image name="star.fill" catalog="system" width="128" height="116"/>
</resources> </resources>
</document> </document>

View File

@ -314,7 +314,7 @@ struct XCBActions {
DispatchQueue.main.async { DispatchQueue.main.async {
show(vc) show(vc)
} }
let alertController = UIAlertController(title: "Follow \(account.realDisplayName)?", message: nil, preferredStyle: .alert) let alertController = UIAlertController(title: "Follow \(account.displayNameWithoutCustomEmoji)?", message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (_) in
performAction(account) performAction(account)
})) }))