forked from shadowfacts/Tusker
parent
1618313742
commit
a363308147
|
@ -86,8 +86,6 @@
|
||||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
||||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
||||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
|
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
|
||||||
D641C77B213CB017004B4513 /* FollowNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D641C77A213CB017004B4513 /* FollowNotificationTableViewCell.xib */; };
|
|
||||||
D641C77D213CB024004B4513 /* FollowNotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77C213CB024004B4513 /* FollowNotificationTableViewCell.swift */; };
|
|
||||||
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; };
|
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; };
|
||||||
D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6434EB2215B1856001A919A /* XCBRequest.swift */; };
|
D6434EB3215B1856001A919A /* XCBRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6434EB2215B1856001A919A /* XCBRequest.swift */; };
|
||||||
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */; };
|
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */; };
|
||||||
|
@ -148,6 +146,8 @@
|
||||||
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 */; };
|
||||||
D6A3BC7D232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */; };
|
D6A3BC7D232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */; };
|
||||||
|
D6A3BC802321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC7E2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift */; };
|
||||||
|
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */; };
|
||||||
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; };
|
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; };
|
||||||
D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */; };
|
D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */; };
|
||||||
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
||||||
|
@ -328,8 +328,6 @@
|
||||||
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
|
||||||
D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = DomainBlocks.plist; sourceTree = "<group>"; };
|
D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = DomainBlocks.plist; sourceTree = "<group>"; };
|
||||||
D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
|
||||||
D641C77A213CB017004B4513 /* FollowNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowNotificationTableViewCell.xib; sourceTree = "<group>"; };
|
|
||||||
D641C77C213CB024004B4513 /* FollowNotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowNotificationTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = "<group>"; };
|
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = "<group>"; };
|
||||||
D6434EB2215B1856001A919A /* XCBRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequest.swift; sourceTree = "<group>"; };
|
D6434EB2215B1856001A919A /* XCBRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCBRequest.swift; sourceTree = "<group>"; };
|
||||||
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageExpandAnimationController.swift; sourceTree = "<group>"; };
|
D646C955213B365700269FB5 /* LargeImageExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageExpandAnimationController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -389,6 +387,8 @@
|
||||||
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>"; };
|
||||||
D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ActionNotificationGroupTableViewCell.xib; sourceTree = "<group>"; };
|
D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ActionNotificationGroupTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
D6A3BC7E2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowNotificationGroupTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowNotificationGroupTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = "<group>"; };
|
D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = "<group>"; };
|
||||||
D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OnboardingViewController.xib; sourceTree = "<group>"; };
|
D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OnboardingViewController.xib; sourceTree = "<group>"; };
|
||||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -785,10 +785,10 @@
|
||||||
D641C78C213DD937004B4513 /* Notifications */ = {
|
D641C78C213DD937004B4513 /* Notifications */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D641C77A213CB017004B4513 /* FollowNotificationTableViewCell.xib */,
|
|
||||||
D641C77C213CB024004B4513 /* FollowNotificationTableViewCell.swift */,
|
|
||||||
D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */,
|
D6A3BC7A232195C600FD64D5 /* ActionNotificationGroupTableViewCell.swift */,
|
||||||
D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */,
|
D6A3BC7B232195C600FD64D5 /* ActionNotificationGroupTableViewCell.xib */,
|
||||||
|
D6A3BC7E2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift */,
|
||||||
|
D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */,
|
||||||
);
|
);
|
||||||
path = Notifications;
|
path = Notifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1346,11 +1346,11 @@
|
||||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */,
|
||||||
D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */,
|
D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */,
|
||||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */,
|
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */,
|
||||||
D641C77B213CB017004B4513 /* FollowNotificationTableViewCell.xib in Resources */,
|
|
||||||
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
|
D6289E84217B795D0003D1D7 /* LargeImageViewController.xib in Resources */,
|
||||||
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
|
D627FF79217E950100CC0648 /* DraftsTableViewController.xib in Resources */,
|
||||||
D67C57B221E28FAD00C3118B /* ComposeStatusReplyView.xib in Resources */,
|
D67C57B221E28FAD00C3118B /* ComposeStatusReplyView.xib in Resources */,
|
||||||
0411610122B442870030A9B7 /* AttachmentViewController.xib in Resources */,
|
0411610122B442870030A9B7 /* AttachmentViewController.xib in Resources */,
|
||||||
|
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */,
|
||||||
D60C07E421E8176B0057FAA8 /* ComposeMediaView.xib in Resources */,
|
D60C07E421E8176B0057FAA8 /* ComposeMediaView.xib in Resources */,
|
||||||
D667E5E12134937B0057A976 /* StatusTableViewCell.xib in Resources */,
|
D667E5E12134937B0057A976 /* StatusTableViewCell.xib in Resources */,
|
||||||
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */,
|
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */,
|
||||||
|
@ -1461,6 +1461,7 @@
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
||||||
D66362712136338600C9CBA2 /* ComposeViewController.swift in Sources */,
|
D66362712136338600C9CBA2 /* ComposeViewController.swift in Sources */,
|
||||||
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */,
|
D6028B9B2150811100F223B9 /* MastodonCache.swift in Sources */,
|
||||||
|
D6A3BC802321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.swift in Sources */,
|
||||||
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
|
||||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
||||||
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
|
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
|
||||||
|
@ -1512,7 +1513,6 @@
|
||||||
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
|
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
|
||||||
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
|
D667E5EB21349EF80057A976 /* ProfileHeaderTableViewCell.swift in Sources */,
|
||||||
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
|
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
|
||||||
D641C77D213CB024004B4513 /* FollowNotificationTableViewCell.swift in Sources */,
|
|
||||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */,
|
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */,
|
||||||
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */,
|
D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */,
|
||||||
D6F1F84D2193B56E00F5FE67 /* Cache.swift in Sources */,
|
D6F1F84D2193B56E00F5FE67 /* Cache.swift in Sources */,
|
||||||
|
|
|
@ -11,11 +11,11 @@ import Pachyderm
|
||||||
|
|
||||||
class NotificationsTableViewController: EnhancedTableViewController {
|
class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
let statusCell = "statusCell"
|
private let statusCell = "statusCell"
|
||||||
let actionGroupCell = "actionGroupCell"
|
private let actionGroupCell = "actionGroupCell"
|
||||||
let followCell = "followCell"
|
private let followGroupCell = "followGroupCell"
|
||||||
|
|
||||||
let groupTypes = [Notification.Kind.favourite, .reblog]
|
let groupTypes = [Notification.Kind.favourite, .reblog, .follow]
|
||||||
|
|
||||||
var groups: [NotificationGroup] = [] {
|
var groups: [NotificationGroup] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
|
@ -50,7 +50,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
tableView.register(UINib(nibName: "StatusTableViewCell", bundle: nil), forCellReuseIdentifier: statusCell)
|
tableView.register(UINib(nibName: "StatusTableViewCell", bundle: nil), forCellReuseIdentifier: statusCell)
|
||||||
tableView.register(UINib(nibName: "ActionNotificationGroupTableViewCell", bundle: nil), forCellReuseIdentifier: actionGroupCell)
|
tableView.register(UINib(nibName: "ActionNotificationGroupTableViewCell", bundle: nil), forCellReuseIdentifier: actionGroupCell)
|
||||||
tableView.register(UINib(nibName: "FollowNotificationTableViewCell", bundle: nil), forCellReuseIdentifier: followCell)
|
tableView.register(UINib(nibName: "FollowNotificationGroupTableViewCell", bundle: nil), forCellReuseIdentifier: followGroupCell)
|
||||||
|
|
||||||
tableView.prefetchDataSource = self
|
tableView.prefetchDataSource = self
|
||||||
|
|
||||||
|
@ -104,15 +104,10 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
case .follow:
|
case .follow:
|
||||||
guard let notification = MastodonCache.notification(for: group.notificationIDs.first!) else { fatalError() }
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "followGroupCell", for: indexPath) as? FollowNotificationGroupTableViewCell else { fatalError() }
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: followCell, for: indexPath) as? FollowNotificationTableViewCell else { fatalError() }
|
cell.updateUI(group: group)
|
||||||
cell.updateUI(for: notification)
|
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
// guard let cell = tableView.dequeueReusableCell(withIdentifier: "followGroupCell", for: indexPath) as? FollowNotificationGroupTableViewCell else { fatalError() }
|
|
||||||
// cell.updateUI(notificationGroup: group)
|
|
||||||
// cell.delegate = self
|
|
||||||
// return cell
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import SwiftSoup
|
||||||
|
|
||||||
class ActionNotificationGroupTableViewCell: UITableViewCell {
|
class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
var delegate: StatusTableViewCellDelegate?
|
var delegate: TuskerNavigationDelegate?
|
||||||
|
|
||||||
@IBOutlet weak var actionImageView: UIImageView!
|
@IBOutlet weak var actionImageView: UIImageView!
|
||||||
@IBOutlet weak var actionAvatarStackView: UIStackView!
|
@IBOutlet weak var actionAvatarStackView: UIStackView!
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// FollowNotificationGroupTableViewCell.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 9/5/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
class FollowNotificationGroupTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
var delegate: TuskerNavigationDelegate?
|
||||||
|
|
||||||
|
@IBOutlet weak var avatarStackView: UIStackView!
|
||||||
|
@IBOutlet weak var actionLabel: UILabel!
|
||||||
|
|
||||||
|
var group: NotificationGroup!
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateUIForPreferences() {
|
||||||
|
let people = group.notificationIDs.compactMap(MastodonCache.notification(for:)).map { $0.account }
|
||||||
|
updateActionLabel(people: people)
|
||||||
|
|
||||||
|
for case let imageView as UIImageView in avatarStackView.arrangedSubviews {
|
||||||
|
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: imageView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUI(group: NotificationGroup) {
|
||||||
|
self.group = group
|
||||||
|
|
||||||
|
let people = group.notificationIDs.compactMap(MastodonCache.notification(for:)).map { $0.account }
|
||||||
|
|
||||||
|
updateActionLabel(people: people)
|
||||||
|
|
||||||
|
avatarStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||||
|
for account in people {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
imageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * 30
|
||||||
|
ImageCache.avatars.get(account.avatar) { (data) in
|
||||||
|
guard let data = data, self.group.id == group.id else { return }
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
imageView.image = UIImage(data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avatarStackView.addArrangedSubview(imageView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
imageView.widthAnchor.constraint(equalToConstant: 30),
|
||||||
|
imageView.heightAnchor.constraint(equalToConstant: 30),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateActionLabel(people: [Account]) {
|
||||||
|
// todo: figure out how to localize this
|
||||||
|
let peopleStr: String
|
||||||
|
switch (people.count) {
|
||||||
|
case 1:
|
||||||
|
peopleStr = people.first!.realDisplayName
|
||||||
|
case 2:
|
||||||
|
peopleStr = people.first!.realDisplayName + " and " + people.last!.realDisplayName
|
||||||
|
default:
|
||||||
|
peopleStr = people.dropLast().map { $0.realDisplayName }.joined(separator: ", ") + ", and " + people.last!.realDisplayName
|
||||||
|
|
||||||
|
}
|
||||||
|
actionLabel.text = "Followed by \(peopleStr)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="98" id="KGk-i7-Jjw" customClass="FollowNotificationGroupTableViewCell" customModule="Tusker" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="98"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="98"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="g8L-M7-dD6">
|
||||||
|
<rect key="frame" x="74" y="11" width="230" height="76"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="xyB-aZ-YhR">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="230" height="30"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="30" id="3ns-8D-P1Q"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Followed by Person 1 and Person 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bHA-9x-pcO">
|
||||||
|
<rect key="frame" x="0.0" y="30" width="198" height="46"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="person.badge.plus.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="7gy-KD-YT1">
|
||||||
|
<rect key="frame" x="36" y="12.5" width="30" height="30.5"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="30" id="gvV-4g-2Xr"/>
|
||||||
|
<constraint firstAttribute="height" constant="30" id="lS8-fq-ptY"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="7gy-KD-YT1" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="1Vb-q3-i8P"/>
|
||||||
|
<constraint firstAttribute="bottomMargin" secondItem="g8L-M7-dD6" secondAttribute="bottom" id="Dzg-eX-ZyM"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="g8L-M7-dD6" secondAttribute="trailing" id="Pg7-9Q-vYV"/>
|
||||||
|
<constraint firstItem="g8L-M7-dD6" firstAttribute="leading" secondItem="7gy-KD-YT1" secondAttribute="trailing" constant="8" id="dCe-Ie-iRs"/>
|
||||||
|
<constraint firstItem="g8L-M7-dD6" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="58" id="lWc-MX-lAl"/>
|
||||||
|
<constraint firstItem="g8L-M7-dD6" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="xUY-IV-Jbu"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="actionLabel" destination="bHA-9x-pcO" id="Woa-25-hgd"/>
|
||||||
|
<outlet property="avatarStackView" destination="xyB-aZ-YhR" id="DDp-5c-Qdo"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="131.8840579710145" y="171.42857142857142"/>
|
||||||
|
</tableViewCell>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="person.badge.plus.fill" catalog="system" width="64" height="58"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
|
@ -1,107 +0,0 @@
|
||||||
//
|
|
||||||
// FollowNotificationTableViewCell.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 9/2/18.
|
|
||||||
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Pachyderm
|
|
||||||
|
|
||||||
class FollowNotificationTableViewCell: UITableViewCell {
|
|
||||||
|
|
||||||
var delegate: StatusTableViewCellDelegate?
|
|
||||||
|
|
||||||
@IBOutlet weak var followLabel: UILabel!
|
|
||||||
@IBOutlet weak var timestampLabel: UILabel!
|
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
|
||||||
@IBOutlet weak var displayNameLabel: UILabel!
|
|
||||||
@IBOutlet weak var usernameLabel: UILabel!
|
|
||||||
|
|
||||||
var notification: Pachyderm.Notification!
|
|
||||||
var accountID: String!
|
|
||||||
|
|
||||||
var avatarURL: URL?
|
|
||||||
var updateTimestampWorkItem: DispatchWorkItem?
|
|
||||||
|
|
||||||
override func awakeFromNib() {
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
avatarImageView.layer.masksToBounds = true
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUI(for notification: Pachyderm.Notification) {
|
|
||||||
self.notification = notification
|
|
||||||
let account = notification.account
|
|
||||||
self.accountID = account.id
|
|
||||||
|
|
||||||
updateUIForPreferences()
|
|
||||||
|
|
||||||
usernameLabel.text = "@\(account.acct)"
|
|
||||||
avatarImageView.image = nil
|
|
||||||
avatarURL = account.avatar
|
|
||||||
ImageCache.avatars.get(account.avatar) { (data) in
|
|
||||||
guard let data = data else { return }
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.avatarImageView.image = UIImage(data: data)
|
|
||||||
self.avatarURL = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateTimestamp()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateUIForPreferences() {
|
|
||||||
let account = MastodonCache.account(for: accountID)!
|
|
||||||
|
|
||||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
|
||||||
followLabel.text = "Followed by \(account.realDisplayName)"
|
|
||||||
displayNameLabel.text = account.realDisplayName
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateTimestamp() {
|
|
||||||
timestampLabel.text = notification.createdAt.timeAgoString()
|
|
||||||
let delay: DispatchTimeInterval?
|
|
||||||
switch notification.createdAt.timeAgo().1 {
|
|
||||||
case .second:
|
|
||||||
delay = .seconds(10)
|
|
||||||
case .minute:
|
|
||||||
delay = .seconds(60)
|
|
||||||
default:
|
|
||||||
delay = nil
|
|
||||||
}
|
|
||||||
if let delay = delay {
|
|
||||||
updateTimestampWorkItem = DispatchWorkItem {
|
|
||||||
self.updateTimestamp()
|
|
||||||
}
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
|
|
||||||
} else {
|
|
||||||
updateTimestampWorkItem = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepareForReuse() {
|
|
||||||
if let url = avatarURL {
|
|
||||||
ImageCache.avatars.cancel(url)
|
|
||||||
}
|
|
||||||
updateTimestampWorkItem?.cancel()
|
|
||||||
updateTimestampWorkItem = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
|
||||||
super.setSelected(selected, animated: animated)
|
|
||||||
|
|
||||||
if selected {
|
|
||||||
delegate?.selected(account: accountID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension FollowNotificationTableViewCell: MenuPreviewProvider {
|
|
||||||
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
|
|
||||||
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14810.11" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
|
||||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14766.13"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
<capability name="iOS 13.0 system colors" minToolsVersion="11.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
|
||||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="FollowNotificationTableViewCell" customModule="Tusker" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="95"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="h8B-F7-Ki1">
|
|
||||||
<rect key="frame" x="16" y="8" width="343" height="79"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Hp-m3-8ey">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="343" height="21"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Followed by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XXP-BS-J1N">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="318.5" height="21"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<color key="textColor" cocoaTouchSystemColor="secondaryLabelColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mKo-pt-Z5M">
|
|
||||||
<rect key="frame" x="318.5" y="0.0" width="24.5" height="21"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<color key="textColor" cocoaTouchSystemColor="secondaryLabelColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="coX-E6-jv2">
|
|
||||||
<rect key="frame" x="0.0" y="29" width="343" height="50"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Qop-Jw-jFp">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="50" id="OgY-SB-V7y"/>
|
|
||||||
<constraint firstAttribute="height" constant="50" id="tbZ-eK-yYq"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HM4-EZ-8FZ">
|
|
||||||
<rect key="frame" x="58" y="0.0" width="107" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lkz-Ko-ILm">
|
|
||||||
<rect key="frame" x="58" y="28.5" width="91" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
|
|
||||||
<color key="textColor" cocoaTouchSystemColor="secondaryLabelColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="HM4-EZ-8FZ" firstAttribute="top" secondItem="coX-E6-jv2" secondAttribute="top" id="8c8-GV-Fti"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="Qop-Jw-jFp" secondAttribute="bottom" id="AA9-89-Sbh"/>
|
|
||||||
<constraint firstItem="lkz-Ko-ILm" firstAttribute="leading" secondItem="Qop-Jw-jFp" secondAttribute="trailing" constant="8" id="a8d-2u-ipQ"/>
|
|
||||||
<constraint firstItem="Qop-Jw-jFp" firstAttribute="leading" secondItem="coX-E6-jv2" secondAttribute="leading" id="gRD-du-B04"/>
|
|
||||||
<constraint firstItem="lkz-Ko-ILm" firstAttribute="top" secondItem="HM4-EZ-8FZ" secondAttribute="bottom" constant="8" id="mx8-R6-9WE"/>
|
|
||||||
<constraint firstItem="Qop-Jw-jFp" firstAttribute="top" secondItem="coX-E6-jv2" secondAttribute="top" id="nzs-jT-Ax2"/>
|
|
||||||
<constraint firstItem="HM4-EZ-8FZ" firstAttribute="leading" secondItem="Qop-Jw-jFp" secondAttribute="trailing" constant="8" id="py1-om-Dsq"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="h8B-F7-Ki1" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="CoY-6b-cpE"/>
|
|
||||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="h8B-F7-Ki1" secondAttribute="trailing" constant="16" id="Tvv-Eu-iFe"/>
|
|
||||||
<constraint firstItem="h8B-F7-Ki1" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="h7Z-bn-SSY"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="h8B-F7-Ki1" secondAttribute="bottom" constant="8" id="vqk-hv-rxs"/>
|
|
||||||
</constraints>
|
|
||||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="avatarImageView" destination="Qop-Jw-jFp" id="Sjo-KQ-qHx"/>
|
|
||||||
<outlet property="displayNameLabel" destination="HM4-EZ-8FZ" id="V2G-a1-e2p"/>
|
|
||||||
<outlet property="followLabel" destination="XXP-BS-J1N" id="266-fO-C0n"/>
|
|
||||||
<outlet property="timestampLabel" destination="mKo-pt-Z5M" id="hxJ-0f-W0C"/>
|
|
||||||
<outlet property="usernameLabel" destination="lkz-Ko-ILm" id="OPO-sE-Ay1"/>
|
|
||||||
</connections>
|
|
||||||
<point key="canvasLocation" x="40.799999999999997" y="73.763118440779621"/>
|
|
||||||
</view>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
Loading…
Reference in New Issue