Compare commits
No commits in common. "2a8e970738937ed3f470a5e0bc851e87b636e150" and "0c06d91f6bbc7278ad01ef234dc76d3cc6c02a23" have entirely different histories.
2a8e970738
...
0c06d91f6b
|
@ -149,6 +149,20 @@ class BookmarksTableViewController: EnhancedTableViewController {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func getSuggestedContextMenuActions(tableView: UITableView, indexPath: IndexPath, point: CGPoint) -> [UIAction] {
|
||||||
|
guard let status = mastodonController.persistentContainer.status(for: statuses[indexPath.row].id) else { return [] }
|
||||||
|
return [
|
||||||
|
UIAction(title: NSLocalizedString("Unbookmark", comment: "unbookmark action title"), image: UIImage(systemName: "bookmark.fill"), identifier: .init("unbookmark"), discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
|
||||||
|
let request = Status.unbookmark(status.id)
|
||||||
|
self.mastodonController.run(request) { (response) in
|
||||||
|
guard case let .success(newStatus, _) = response else { fatalError() }
|
||||||
|
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus, incrementReferenceCount: false)
|
||||||
|
self.statuses.remove(at: indexPath.row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BookmarksTableViewController: StatusTableViewCellDelegate {
|
extension BookmarksTableViewController: StatusTableViewCellDelegate {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Pachyderm
|
||||||
|
|
||||||
protocol MenuPreviewProvider {
|
protocol MenuPreviewProvider {
|
||||||
|
|
||||||
typealias PreviewProviders = (content: UIContextMenuContentPreviewProvider, actions: () -> [UIMenuElement])
|
typealias PreviewProviders = (content: UIContextMenuContentPreviewProvider, actions: () -> [UIAction])
|
||||||
|
|
||||||
var navigationDelegate: TuskerNavigationDelegate? { get }
|
var navigationDelegate: TuskerNavigationDelegate? { get }
|
||||||
|
|
||||||
|
@ -28,142 +28,50 @@ extension MenuPreviewProvider {
|
||||||
|
|
||||||
private var mastodonController: MastodonController? { navigationDelegate?.apiController }
|
private var mastodonController: MastodonController? { navigationDelegate?.apiController }
|
||||||
|
|
||||||
// Default no-op implementation
|
func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIAction] {
|
||||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIMenuElement] {
|
|
||||||
guard let mastodonController = mastodonController,
|
guard let mastodonController = mastodonController,
|
||||||
let account = mastodonController.persistentContainer.account(for: accountID) else { return [] }
|
let account = mastodonController.persistentContainer.account(for: accountID) else { return [] }
|
||||||
|
return [
|
||||||
var actionsSection: [UIMenuElement] = [
|
|
||||||
createAction(identifier: "sendmessage", title: "Send Message", systemImageName: "envelope", handler: { (_) in
|
createAction(identifier: "sendmessage", title: "Send Message", systemImageName: "envelope", handler: { (_) in
|
||||||
self.navigationDelegate?.compose(mentioning: account.acct)
|
self.navigationDelegate?.compose(mentioning: account.acct)
|
||||||
}),
|
}),
|
||||||
]
|
createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
|
||||||
|
self.navigationDelegate?.selected(url: account.url)
|
||||||
// todo: handle pre-iOS 14
|
}),
|
||||||
if accountID != mastodonController.account.id,
|
|
||||||
#available(iOS 14.0, *) {
|
|
||||||
actionsSection.append(UIDeferredMenuElement({ (elementHandler) in
|
|
||||||
guard let mastodonController = self.mastodonController else {
|
|
||||||
elementHandler([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let request = Client.getRelationships(accounts: [account.id])
|
|
||||||
mastodonController.run(request) { (response) in
|
|
||||||
if case let .success(results, _) = response,
|
|
||||||
let relationship = results.first {
|
|
||||||
let following = relationship.following
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
elementHandler([
|
|
||||||
createAction(identifier: "follow", title: following ? "Unfollow" : "Follow", systemImageName: following ? "person.badge.minus" : "person.badge.minus", handler: { (_) in
|
|
||||||
let request = (following ? Account.unfollow : Account.follow)(accountID)
|
|
||||||
mastodonController.run(request) { (_) in
|
|
||||||
}
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
let shareSection = [
|
|
||||||
openInSafariAction(url: account.url),
|
|
||||||
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
||||||
self.navigationDelegate?.showMoreOptions(forAccount: accountID, sourceView: sourceView)
|
self.navigationDelegate?.showMoreOptions(forAccount: accountID, sourceView: sourceView)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
return [
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func actionsForURL(_ url: URL, sourceView: UIView?) -> [UIAction] {
|
func actionsForURL(_ url: URL, sourceView: UIView?) -> [UIAction] {
|
||||||
return [
|
return [
|
||||||
openInSafariAction(url: url),
|
createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
|
||||||
|
self.navigationDelegate?.selected(url: url)
|
||||||
|
}),
|
||||||
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
||||||
self.navigationDelegate?.showMoreOptions(forURL: url, sourceView: sourceView)
|
self.navigationDelegate?.showMoreOptions(forURL: url, sourceView: sourceView)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
func actionsForHashtag(_ hashtag: Hashtag, sourceView: UIView?) -> [UIMenuElement] {
|
func actionsForHashtag(_ hashtag: Hashtag, sourceView: UIView?) -> [UIAction] {
|
||||||
let account = mastodonController!.accountInfo!
|
return actionsForURL(hashtag.url, sourceView: sourceView)
|
||||||
let saved = SavedDataManager.shared.isSaved(hashtag: hashtag, for: account)
|
|
||||||
|
|
||||||
let actionsSection = [
|
|
||||||
createAction(identifier: "save", title: saved ? "Unsave Hashtag" : "Save Hashtag", systemImageName: "number", handler: { (_) in
|
|
||||||
if saved {
|
|
||||||
SavedDataManager.shared.remove(hashtag: hashtag, for: account)
|
|
||||||
} else {
|
|
||||||
SavedDataManager.shared.add(hashtag: hashtag, for: account)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
let shareSection = actionsForURL(hashtag.url, sourceView: sourceView)
|
|
||||||
|
|
||||||
return [
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func actionsForStatus(statusID: String, sourceView: UIView?) -> [UIMenuElement] {
|
func actionsForStatus(statusID: String, sourceView: UIView?) -> [UIAction] {
|
||||||
guard let mastodonController = mastodonController,
|
guard let mastodonController = mastodonController,
|
||||||
let status = mastodonController.persistentContainer.status(for: statusID) else { return [] }
|
let status = mastodonController.persistentContainer.status(for: statusID) else { return [] }
|
||||||
let bookmarked = status.bookmarked ?? false
|
return [
|
||||||
let muted = status.muted
|
|
||||||
|
|
||||||
var actionsSection = [
|
|
||||||
createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in
|
createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in
|
||||||
self.navigationDelegate?.reply(to: statusID)
|
self.navigationDelegate?.reply(to: statusID)
|
||||||
}),
|
}),
|
||||||
createAction(identifier: "bookmark", title: bookmarked ? "Unbookmark" : "Bookmark", systemImageName: bookmarked ? "bookmark.fill" : "bookmark", handler: { (_) in
|
createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
|
||||||
let request = (bookmarked ? Status.unbookmark : Status.bookmark)(statusID)
|
self.navigationDelegate?.selected(url: status.url!)
|
||||||
self.mastodonController?.run(request) { (response) in
|
|
||||||
if case let .success(status, _) = response {
|
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
createAction(identifier: "mute", title: muted ? "Unmute" : "Mute", systemImageName: muted ? "speaker" : "speaker.slash", handler: { (_) in
|
|
||||||
let request = (muted ? Status.unmuteConversation : Status.muteConversation)(statusID)
|
|
||||||
self.mastodonController?.run(request) { (response) in
|
|
||||||
if case let .success(status, _) = response {
|
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
if mastodonController.account != nil && mastodonController.account.id == status.account.id {
|
|
||||||
let pinned = status.pinned ?? false
|
|
||||||
actionsSection.append(createAction(identifier: "", title: pinned ? "Unpin" : "Pin", systemImageName: pinned ? "pin.slash" : "pin", handler: { (_) in
|
|
||||||
let request = (pinned ? Status.unpin : Status.pin)(statusID)
|
|
||||||
self.mastodonController?.run(request, completion: { (response) in
|
|
||||||
if case let .success(status, _) = response {
|
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
let shareSection = [
|
|
||||||
openInSafariAction(url: status.url!),
|
|
||||||
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in
|
||||||
self.navigationDelegate?.showMoreOptions(forStatus: statusID, sourceView: sourceView)
|
self.navigationDelegate?.showMoreOptions(forStatus: statusID, sourceView: sourceView)
|
||||||
}),
|
})
|
||||||
]
|
|
||||||
|
|
||||||
return [
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,12 +79,6 @@ extension MenuPreviewProvider {
|
||||||
return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
|
return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openInSafariAction(url: URL) -> UIAction {
|
|
||||||
return createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in
|
|
||||||
self.navigationDelegate?.selected(url: url)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LargeImageViewController: CustomPreviewPresenting {
|
extension LargeImageViewController: CustomPreviewPresenting {
|
||||||
|
|
|
@ -177,41 +177,32 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
guard let status = apiController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
guard let status = apiController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
||||||
guard let url = status.url else { fatalError("Missing url for status \(statusID)") }
|
guard let url = status.url else { fatalError("Missing url for status \(statusID)") }
|
||||||
|
|
||||||
// on iOS 14+, all these custom actions are in the context menu and don't need to be in the share sheet
|
var customActivites: [UIActivity] = [
|
||||||
if #available(iOS 14.0, *) {
|
OpenInSafariActivity(),
|
||||||
return UIActivityViewController(activityItems: [url, StatusActivityItemSource(status)], applicationActivities: nil)
|
(status.bookmarked ?? false) ? UnbookmarkStatusActivity() : BookmarkStatusActivity(),
|
||||||
} else {
|
status.muted ? UnmuteConversationActivity() : MuteConversationActivity(),
|
||||||
var customActivites: [UIActivity] = [
|
]
|
||||||
OpenInSafariActivity(),
|
|
||||||
(status.bookmarked ?? false) ? UnbookmarkStatusActivity() : BookmarkStatusActivity(),
|
|
||||||
status.muted ? UnmuteConversationActivity() : MuteConversationActivity(),
|
|
||||||
]
|
|
||||||
|
|
||||||
if apiController.account != nil, status.account.id == apiController.account.id {
|
if apiController.account != nil, status.account.id == apiController.account.id {
|
||||||
let pinned = status.pinned ?? false
|
let pinned = status.pinned ?? false
|
||||||
customActivites.insert(pinned ? UnpinStatusActivity() : PinStatusActivity(), at: 1)
|
customActivites.insert(pinned ? UnpinStatusActivity() : PinStatusActivity(), at: 1)
|
||||||
}
|
|
||||||
|
|
||||||
let activityController = UIActivityViewController(activityItems: [url, StatusActivityItemSource(status)], applicationActivities: customActivites)
|
|
||||||
activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: url)
|
|
||||||
return activityController
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let activityController = UIActivityViewController(activityItems: [url, StatusActivityItemSource(status)], applicationActivities: customActivites)
|
||||||
|
activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: url)
|
||||||
|
return activityController
|
||||||
}
|
}
|
||||||
|
|
||||||
private func moreOptions(forAccount accountID: String) -> UIActivityViewController {
|
private func moreOptions(forAccount accountID: String) -> UIActivityViewController {
|
||||||
guard let account = apiController.persistentContainer.account(for: accountID) else { fatalError("Missing cached account \(accountID)") }
|
guard let account = apiController.persistentContainer.account(for: accountID) else { fatalError("Missing cached account \(accountID)") }
|
||||||
|
|
||||||
if #available(iOS 14.0, *) {
|
let customActivities: [UIActivity] = [
|
||||||
return UIActivityViewController(activityItems: [account.url, AccountActivityItemSource(account)], applicationActivities: nil)
|
OpenInSafariActivity(),
|
||||||
} else {
|
]
|
||||||
let customActivities: [UIActivity] = [
|
|
||||||
OpenInSafariActivity(),
|
|
||||||
]
|
|
||||||
|
|
||||||
let activityController = UIActivityViewController(activityItems: [account.url, AccountActivityItemSource(account)], applicationActivities: customActivities)
|
let activityController = UIActivityViewController(activityItems: [account.url, AccountActivityItemSource(account)], applicationActivities: customActivities)
|
||||||
activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url)
|
activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url)
|
||||||
return activityController
|
return activityController
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func showMoreOptions(forStatus statusID: String, sourceView: UIView?) {
|
func showMoreOptions(forStatus statusID: String, sourceView: UIView?) {
|
||||||
|
|
|
@ -27,9 +27,6 @@ class ContentTextView: LinkTextView {
|
||||||
|
|
||||||
delegate = self
|
delegate = self
|
||||||
|
|
||||||
// Disable layer masking, otherwise the context menu opening animation
|
|
||||||
// may be clipped if it's at an edge of the text view
|
|
||||||
layer.masksToBounds = false
|
|
||||||
addInteraction(UIContextMenuInteraction(delegate: self))
|
addInteraction(UIContextMenuInteraction(delegate: self))
|
||||||
|
|
||||||
textDragInteraction?.isEnabled = false
|
textDragInteraction?.isEnabled = false
|
||||||
|
@ -256,18 +253,12 @@ extension ContentTextView: MenuPreviewProvider {
|
||||||
extension ContentTextView: UIContextMenuInteractionDelegate {
|
extension ContentTextView: UIContextMenuInteractionDelegate {
|
||||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
if let (link, range) = getLinkAtPoint(location) {
|
if let (link, range) = getLinkAtPoint(location) {
|
||||||
// Determine the line rects that the link takes up in the coordinate space of this view
|
|
||||||
var rects = [CGRect]()
|
|
||||||
layoutManager.enumerateEnclosingRects(forGlyphRange: range, withinSelectedGlyphRange: NSRange(location: NSNotFound, length: 0), in: textContainer) { (rect, stop) in
|
|
||||||
rects.append(rect)
|
|
||||||
}
|
|
||||||
|
|
||||||
let preview: UIContextMenuContentPreviewProvider = {
|
let preview: UIContextMenuContentPreviewProvider = {
|
||||||
self.getViewController(forLink: link, inRange: range)
|
self.getViewController(forLink: link, inRange: range)
|
||||||
}
|
}
|
||||||
let actions: UIContextMenuActionProvider = { (_) in
|
let actions: UIContextMenuActionProvider = { (_) in
|
||||||
let text = (self.text as NSString).substring(with: range)
|
let text = (self.text as NSString).substring(with: range)
|
||||||
let actions: [UIMenuElement]
|
let actions: [UIAction]
|
||||||
if let mention = self.getMention(for: link, text: text) {
|
if let mention = self.getMention(for: link, text: text) {
|
||||||
actions = self.actionsForProfile(accountID: mention.id, sourceView: self)
|
actions = self.actionsForProfile(accountID: mention.id, sourceView: self)
|
||||||
} else if let tag = self.getHashtag(for: link, text: text) {
|
} else if let tag = self.getHashtag(for: link, text: text) {
|
||||||
|
@ -277,68 +268,11 @@ extension ContentTextView: UIContextMenuInteractionDelegate {
|
||||||
}
|
}
|
||||||
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
|
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
|
||||||
}
|
}
|
||||||
|
return UIContextMenuConfiguration(identifier: nil, previewProvider: preview, actionProvider: actions)
|
||||||
// Use a custom UIContentMenuConfiguration subclass to pass the text line rect information
|
|
||||||
// to the `contextMenuInteraction(_:previewForHighlightingMenuWithConfiguration:)` method.
|
|
||||||
let configuration = ContextMenuConfiguration(identifier: nil, previewProvider: preview, actionProvider: actions)
|
|
||||||
configuration.textLineRects = rects
|
|
||||||
return configuration
|
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
|
||||||
// If there isn't custom text line rect data, use the default system-generated preview.
|
|
||||||
guard let config = configuration as? ContextMenuConfiguration,
|
|
||||||
let rects = config.textLineRects,
|
|
||||||
rects.count > 0 else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the smallest rect enclosing all of the text line rects, in the coordinate space of this view.
|
|
||||||
var minX: CGFloat = .greatestFiniteMagnitude, maxX: CGFloat = .leastNonzeroMagnitude, minY: CGFloat = .greatestFiniteMagnitude, maxY: CGFloat = .leastNonzeroMagnitude
|
|
||||||
for rect in rects {
|
|
||||||
minX = min(rect.minX, minX)
|
|
||||||
maxX = max(rect.maxX, maxX)
|
|
||||||
minY = min(rect.minY, minY)
|
|
||||||
maxY = max(rect.maxY, maxY)
|
|
||||||
}
|
|
||||||
let rectEnclosingTextLineRects = CGRect(x: minX, y: minY, width: maxX - minX, height: maxY - minY)
|
|
||||||
|
|
||||||
// Try to create a snapshot view of this view that only shows the minimum
|
|
||||||
// rectangle necessary to fully display the link text (reduces the likelihood that
|
|
||||||
// other text will be displayed alongside it).
|
|
||||||
// If a snapshot view cannot be created, we bail and use the system-provided preview.
|
|
||||||
guard let snapshot = self.resizableSnapshotView(from: rectEnclosingTextLineRects, afterScreenUpdates: false, withCapInsets: .zero) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the textLineRects from the context menu configuration to be in the
|
|
||||||
// coordinate space of the snapshot view. The snapshot view is created from
|
|
||||||
// rectEnclosingTextLineRects, which means that, while its size is the same as the
|
|
||||||
// enclosing rect, its coordinate space is relative to this text views by rectEnclosingTextLineRects.origin.
|
|
||||||
// Since the text line rects passed to UIPreviewParameters need to be in the coordinate space of
|
|
||||||
// the preview view, we subtract the origin position from each rect to convert to the snapshot view's
|
|
||||||
// coordinate space.
|
|
||||||
let rectsInCoordinateSpaceOfEnclosingRect = rects.map {
|
|
||||||
$0.offsetBy(dx: -rectEnclosingTextLineRects.minX, dy: -rectEnclosingTextLineRects.minY)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The preview parameters describe how the preview view is shown inside the prev.
|
|
||||||
let parameters = UIPreviewParameters(textLineRects: rectsInCoordinateSpaceOfEnclosingRect as [NSValue])
|
|
||||||
// todo: parameters.visiblePath around text
|
|
||||||
|
|
||||||
// The center point of the the minimum enclosing rect in our coordinate space is the point where the
|
|
||||||
// center of the preview should be, since that's also in this view's coordinate space.
|
|
||||||
let rectsCenter = CGPoint(x: rectEnclosingTextLineRects.midX, y: rectEnclosingTextLineRects.midY)
|
|
||||||
|
|
||||||
// The preview target describes how the preview is positioned.
|
|
||||||
let target = UIPreviewTarget(container: self, center: rectsCenter)
|
|
||||||
|
|
||||||
return UITargetedPreview(view: snapshot, parameters: parameters, target: target)
|
|
||||||
}
|
|
||||||
|
|
||||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
||||||
if let viewController = animator.previewViewController {
|
if let viewController = animator.previewViewController {
|
||||||
animator.preferredCommitStyle = .pop
|
animator.preferredCommitStyle = .pop
|
||||||
|
@ -347,10 +281,4 @@ extension ContentTextView: UIContextMenuInteractionDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to pass text line rect data between `contextMenuInteraction(_:configurationForMenuAtLocation:)` and `contextMenuInteraction(_:previewForHighlightingMenuWithConfiguration:)`
|
|
||||||
fileprivate class ContextMenuConfiguration: UIContextMenuConfiguration {
|
|
||||||
/// The line rects of the source of this context menu configuration in the coordinate space of the preview target view.
|
|
||||||
var textLineRects: [CGRect]?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
@IBOutlet weak var fieldsStackView: UIStackView!
|
@IBOutlet weak var fieldsStackView: UIStackView!
|
||||||
@IBOutlet weak var fieldNamesStackView: UIStackView!
|
@IBOutlet weak var fieldNamesStackView: UIStackView!
|
||||||
@IBOutlet weak var fieldValuesStackView: UIStackView!
|
@IBOutlet weak var fieldValuesStackView: UIStackView!
|
||||||
@IBOutlet weak var moreButton: VisualEffectImageButton!
|
@IBOutlet weak var moreButtonVisualEffectView: UIVisualEffectView!
|
||||||
|
|
||||||
var accountID: String!
|
var accountID: String!
|
||||||
|
|
||||||
|
@ -45,16 +45,15 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
avatarImageView.isUserInteractionEnabled = true
|
avatarImageView.isUserInteractionEnabled = true
|
||||||
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
||||||
headerImageView.isUserInteractionEnabled = true
|
headerImageView.isUserInteractionEnabled = true
|
||||||
|
moreButtonVisualEffectView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(morePressed)))
|
||||||
|
|
||||||
moreButton.layer.cornerRadius = 16
|
let maskLayer = CAShapeLayer()
|
||||||
moreButton.layer.masksToBounds = true
|
maskLayer.frame = moreButtonVisualEffectView.bounds
|
||||||
|
maskLayer.path = CGPath(ellipseIn: moreButtonVisualEffectView.bounds, transform: nil)
|
||||||
|
moreButtonVisualEffectView.layer.mask = maskLayer
|
||||||
|
|
||||||
if #available(iOS 13.4, *) {
|
if #available(iOS 13.4, *) {
|
||||||
moreButton.addInteraction(UIPointerInteraction(delegate: self))
|
moreButtonVisualEffectView.addInteraction(UIPointerInteraction(delegate: self))
|
||||||
}
|
|
||||||
if #available(iOS 14.0, *) {
|
|
||||||
moreButton.showsMenuAsPrimaryAction = true
|
|
||||||
moreButton.isContextMenuInteractionEnabled = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||||
|
@ -85,10 +84,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if #available(iOS 14.0, *) {
|
|
||||||
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: actionsForProfile(accountID: accountID, sourceView: moreButton))
|
|
||||||
}
|
|
||||||
|
|
||||||
noteTextView.navigationDelegate = delegate
|
noteTextView.navigationDelegate = delegate
|
||||||
noteTextView.setTextFromHtml(account.note)
|
noteTextView.setTextFromHtml(account.note)
|
||||||
noteTextView.setEmojis(account.emojis)
|
noteTextView.setEmojis(account.emojis)
|
||||||
|
@ -154,7 +149,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
headerRequest?.cancel()
|
headerRequest?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func morePressed(_ sender: Any) {
|
@objc func morePressed() {
|
||||||
delegate?.showMoreOptions(cell: self)
|
delegate?.showMoreOptions(cell: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,16 +165,11 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 13.4, *)
|
|
||||||
extension ProfileHeaderTableViewCell: UIPointerInteractionDelegate {
|
extension ProfileHeaderTableViewCell: UIPointerInteractionDelegate {
|
||||||
|
@available(iOS 13.4, *)
|
||||||
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
|
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
|
||||||
let preview = UITargetedPreview(view: moreButton)
|
let preview = UITargetedPreview(view: moreButtonVisualEffectView)
|
||||||
return UIPointerStyle(effect: .lift(preview), shape: .none)
|
let rect = CGRect(x: moreButtonVisualEffectView.frame.minX - 4, y: moreButtonVisualEffectView.frame.minY - 4, width: moreButtonVisualEffectView.frame.width + 8, height: moreButtonVisualEffectView.frame.height + 8)
|
||||||
}
|
return UIPointerStyle(effect: .highlight(preview), shape: .roundedRect(rect, radius: 4))
|
||||||
}
|
|
||||||
|
|
||||||
extension ProfileHeaderTableViewCell: MenuPreviewProvider {
|
|
||||||
var navigationDelegate: TuskerNavigationDelegate? {
|
|
||||||
delegate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<?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="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" 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>
|
||||||
<deployment identifier="iOS"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.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>
|
||||||
<objects>
|
<objects>
|
||||||
|
@ -39,7 +37,7 @@
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="tH8-sR-DHC" firstAttribute="centerX" secondItem="KyB-ey-l11" secondAttribute="centerX" id="KT6-FP-LsA"/>
|
<constraint firstItem="tH8-sR-DHC" firstAttribute="centerX" secondItem="KyB-ey-l11" secondAttribute="centerX" id="KT6-FP-LsA"/>
|
||||||
<constraint firstAttribute="height" constant="120" id="LVm-OC-cGm"/>
|
<constraint firstAttribute="height" constant="120" id="LVm-OC-cGm"/>
|
||||||
|
@ -56,7 +54,7 @@
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MIj-OR-NOR">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MIj-OR-NOR">
|
||||||
<rect key="frame" x="144" y="190" width="215" height="18"/>
|
<rect key="frame" x="144" y="190" width="215" height="18"/>
|
||||||
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="DfO-uD-UNI">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="DfO-uD-UNI">
|
||||||
|
@ -65,13 +63,13 @@
|
||||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Follows you" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a32-1a-xXZ">
|
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Follows you" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a32-1a-xXZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="75.5" height="0.0"/>
|
<rect key="frame" x="0.0" y="0.0" width="75.5" height="0.0"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bnc-3t-t7t" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bnc-3t-t7t" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="335.5" height="12"/>
|
<rect key="frame" x="0.0" y="0.0" width="337" height="12"/>
|
||||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||||
<color key="textColor" systemColor="labelColor"/>
|
<color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
</textView>
|
</textView>
|
||||||
|
@ -92,24 +90,50 @@
|
||||||
<constraint firstItem="oza-9d-8v4" firstAttribute="width" relation="greaterThanOrEqual" secondItem="pV2-Mz-54W" secondAttribute="width" multiplier="0.5" id="Zbr-l3-Lff"/>
|
<constraint firstItem="oza-9d-8v4" firstAttribute="width" relation="greaterThanOrEqual" secondItem="pV2-Mz-54W" secondAttribute="width" multiplier="0.5" id="Zbr-l3-Lff"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</stackView>
|
</stackView>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7wb-qe-IRt" customClass="VisualEffectImageButton" customModule="Tusker" customModuleProvider="target">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mQY-XN-PfZ">
|
||||||
<rect key="frame" x="335" y="110" width="32" height="32"/>
|
<rect key="frame" x="335" y="110" width="32" height="32"/>
|
||||||
<accessibility key="accessibilityConfiguration">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0Ol-1d-la6">
|
||||||
<accessibilityTraits key="traits" button="YES"/>
|
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||||
</accessibility>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t0d-eE-mbc">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="TgJ-FF-QyB">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ellipsis" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cLs-dC-SWU">
|
||||||
|
<rect key="frame" x="2" y="12.5" width="28" height="7"/>
|
||||||
|
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="24"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="cLs-dC-SWU" firstAttribute="leading" secondItem="TgJ-FF-QyB" secondAttribute="leading" constant="2" id="7nV-7d-GAY"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="cLs-dC-SWU" secondAttribute="bottom" constant="2" id="8sP-mZ-ZSQ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="cLs-dC-SWU" secondAttribute="trailing" constant="2" id="iBQ-oA-yOm"/>
|
||||||
|
<constraint firstItem="cLs-dC-SWU" firstAttribute="top" secondItem="TgJ-FF-QyB" secondAttribute="top" constant="2" id="jSB-2f-sZF"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<vibrancyEffect style="label">
|
||||||
|
<blurEffect style="prominent"/>
|
||||||
|
</vibrancyEffect>
|
||||||
|
</visualEffectView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="t0d-eE-mbc" firstAttribute="leading" secondItem="0Ol-1d-la6" secondAttribute="leading" id="6Py-U4-Jlo"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="t0d-eE-mbc" secondAttribute="bottom" id="OT5-Yh-eiG"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="t0d-eE-mbc" secondAttribute="trailing" id="a8T-dS-dc8"/>
|
||||||
|
<constraint firstItem="t0d-eE-mbc" firstAttribute="top" secondItem="0Ol-1d-la6" secondAttribute="top" id="xKq-qM-vmk"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="32" id="vwR-Sm-L8e"/>
|
<constraint firstAttribute="height" constant="32" id="Zye-sh-FIH"/>
|
||||||
<constraint firstAttribute="width" constant="32" id="w1W-PQ-czc"/>
|
<constraint firstAttribute="width" constant="32" id="hpF-s0-dbt"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<userDefinedRuntimeAttributes>
|
<blurEffect style="prominent"/>
|
||||||
<userDefinedRuntimeAttribute type="image" keyPath="image" value="ellipsis" catalog="system"/>
|
</visualEffectView>
|
||||||
</userDefinedRuntimeAttributes>
|
|
||||||
<connections>
|
|
||||||
<action selector="morePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="7vJ-y3-FdZ"/>
|
|
||||||
</connections>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="Fw7-OL-iy5" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="0fI-0y-cXG"/>
|
<constraint firstItem="Fw7-OL-iy5" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="0fI-0y-cXG"/>
|
||||||
|
@ -122,7 +146,7 @@
|
||||||
<constraint firstItem="Fw7-OL-iy5" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="LqH-lE-AIe"/>
|
<constraint firstItem="Fw7-OL-iy5" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="LqH-lE-AIe"/>
|
||||||
<constraint firstItem="KyB-ey-l11" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="NN7-5B-k1Q"/>
|
<constraint firstItem="KyB-ey-l11" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="NN7-5B-k1Q"/>
|
||||||
<constraint firstItem="MIj-OR-NOR" firstAttribute="bottom" secondItem="tH8-sR-DHC" secondAttribute="bottom" id="PhQ-El-olR"/>
|
<constraint firstItem="MIj-OR-NOR" firstAttribute="bottom" secondItem="tH8-sR-DHC" secondAttribute="bottom" id="PhQ-El-olR"/>
|
||||||
<constraint firstItem="Fw7-OL-iy5" firstAttribute="bottom" secondItem="7wb-qe-IRt" secondAttribute="bottom" constant="8" id="VY4-aX-YID"/>
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="mQY-XN-PfZ" secondAttribute="trailing" constant="8" id="TZn-8m-0Wq"/>
|
||||||
<constraint firstItem="sHU-GU-klv" firstAttribute="top" secondItem="DfO-uD-UNI" secondAttribute="bottom" constant="8" id="Vza-1s-qbG"/>
|
<constraint firstItem="sHU-GU-klv" firstAttribute="top" secondItem="DfO-uD-UNI" secondAttribute="bottom" constant="8" id="Vza-1s-qbG"/>
|
||||||
<constraint firstAttribute="trailingMargin" secondItem="sHU-GU-klv" secondAttribute="trailing" id="XJa-zP-Ma2"/>
|
<constraint firstAttribute="trailingMargin" secondItem="sHU-GU-klv" secondAttribute="trailing" id="XJa-zP-Ma2"/>
|
||||||
<constraint firstItem="sHU-GU-klv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" id="cSX-WD-2aJ"/>
|
<constraint firstItem="sHU-GU-klv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" id="cSX-WD-2aJ"/>
|
||||||
|
@ -132,9 +156,10 @@
|
||||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="sHU-GU-klv" secondAttribute="bottom" constant="8" id="iRf-l0-ZZX"/>
|
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="sHU-GU-klv" secondAttribute="bottom" constant="8" id="iRf-l0-ZZX"/>
|
||||||
<constraint firstItem="MIj-OR-NOR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="LjK-72-Bez" secondAttribute="bottom" id="nMM-6t-bjX"/>
|
<constraint firstItem="MIj-OR-NOR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="LjK-72-Bez" secondAttribute="bottom" id="nMM-6t-bjX"/>
|
||||||
<constraint firstItem="DfO-uD-UNI" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="pqd-E3-Aw4"/>
|
<constraint firstItem="DfO-uD-UNI" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="pqd-E3-Aw4"/>
|
||||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="7wb-qe-IRt" secondAttribute="trailing" constant="8" id="qvF-dI-4qY"/>
|
<constraint firstItem="LjK-72-Bez" firstAttribute="top" secondItem="mQY-XN-PfZ" secondAttribute="bottom" constant="16" id="rTO-fy-u0V"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="avatarContainerView" destination="KyB-ey-l11" id="45s-jV-l8L"/>
|
<outlet property="avatarContainerView" destination="KyB-ey-l11" id="45s-jV-l8L"/>
|
||||||
<outlet property="avatarImageView" destination="tH8-sR-DHC" id="6ll-yL-g1o"/>
|
<outlet property="avatarImageView" destination="tH8-sR-DHC" id="6ll-yL-g1o"/>
|
||||||
|
@ -144,7 +169,7 @@
|
||||||
<outlet property="fieldsStackView" destination="sHU-GU-klv" id="Gli-Gf-Ubh"/>
|
<outlet property="fieldsStackView" destination="sHU-GU-klv" id="Gli-Gf-Ubh"/>
|
||||||
<outlet property="followsYouLabel" destination="a32-1a-xXZ" id="phY-0L-NnN"/>
|
<outlet property="followsYouLabel" destination="a32-1a-xXZ" id="phY-0L-NnN"/>
|
||||||
<outlet property="headerImageView" destination="Fw7-OL-iy5" id="6sv-E5-D73"/>
|
<outlet property="headerImageView" destination="Fw7-OL-iy5" id="6sv-E5-D73"/>
|
||||||
<outlet property="moreButton" destination="7wb-qe-IRt" id="CIB-zo-ASU"/>
|
<outlet property="moreButtonVisualEffectView" destination="mQY-XN-PfZ" id="t7l-wg-nj0"/>
|
||||||
<outlet property="noteTextView" destination="bnc-3t-t7t" id="dV2-7U-gSd"/>
|
<outlet property="noteTextView" destination="bnc-3t-t7t" id="dV2-7U-gSd"/>
|
||||||
<outlet property="usernameLabel" destination="MIj-OR-NOR" id="e1I-N7-rKx"/>
|
<outlet property="usernameLabel" destination="MIj-OR-NOR" id="e1I-N7-rKx"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
@ -153,14 +178,5 @@
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
||||||
<systemColor name="labelColor">
|
|
||||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</systemColor>
|
|
||||||
<systemColor name="secondaryLabelColor">
|
|
||||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
</systemColor>
|
|
||||||
<systemColor name="systemBackgroundColor">
|
|
||||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</systemColor>
|
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -178,11 +178,6 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
collapsible = state.collapsible!
|
collapsible = state.collapsible!
|
||||||
setCollapsed(state.collapsed!, animated: false)
|
setCollapsed(state.collapsed!, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if #available(iOS 14.0, *) {
|
|
||||||
moreButton.showsMenuAsPrimaryAction = true
|
|
||||||
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: actionsForStatus(statusID: statusID, sourceView: moreButton))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatusState(status: StatusMO) {
|
func updateStatusState(status: StatusMO) {
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
//
|
|
||||||
// VisualEffectImageButton.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 6/26/20.
|
|
||||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class VisualEffectImageButton: UIControl {
|
|
||||||
|
|
||||||
@IBInspectable
|
|
||||||
var image: UIImage! {
|
|
||||||
didSet {
|
|
||||||
imageView?.image = image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu: UIMenu?
|
|
||||||
|
|
||||||
private(set) var imageView: UIImageView!
|
|
||||||
|
|
||||||
override func awakeFromNib() {
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
let blur = UIBlurEffect(style: .prominent)
|
|
||||||
let blurView = UIVisualEffectView(effect: blur)
|
|
||||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
let vibrancy = UIVibrancyEffect(blurEffect: blur, style: .label)
|
|
||||||
let vibrancyView = UIVisualEffectView(effect: vibrancy)
|
|
||||||
vibrancyView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
imageView = UIImageView(image: self.image)
|
|
||||||
imageView.contentMode = .scaleAspectFit
|
|
||||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
vibrancyView.contentView.addSubview(imageView)
|
|
||||||
blurView.contentView.addSubview(vibrancyView)
|
|
||||||
addSubview(blurView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
|
|
||||||
blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
|
|
||||||
blurView.topAnchor.constraint(equalTo: self.topAnchor),
|
|
||||||
blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
|
|
||||||
vibrancyView.leadingAnchor.constraint(equalTo: blurView.leadingAnchor),
|
|
||||||
vibrancyView.trailingAnchor.constraint(equalTo: blurView.trailingAnchor),
|
|
||||||
vibrancyView.topAnchor.constraint(equalTo: blurView.topAnchor),
|
|
||||||
vibrancyView.bottomAnchor.constraint(equalTo: blurView.bottomAnchor),
|
|
||||||
imageView.leadingAnchor.constraint(equalTo: vibrancyView.leadingAnchor, constant: 2),
|
|
||||||
imageView.trailingAnchor.constraint(equalTo: vibrancyView.trailingAnchor, constant: -2),
|
|
||||||
imageView.topAnchor.constraint(equalTo: vibrancyView.topAnchor, constant: 2),
|
|
||||||
imageView.bottomAnchor.constraint(equalTo: vibrancyView.bottomAnchor, constant: -2),
|
|
||||||
])
|
|
||||||
|
|
||||||
addInteraction(UIContextMenuInteraction(delegate: self))
|
|
||||||
|
|
||||||
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func onTap() {
|
|
||||||
sendActions(for: .touchUpInside)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
|
||||||
guard let menu = menu else { return nil }
|
|
||||||
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (_) -> UIMenu? in
|
|
||||||
return menu
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue