diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
index 2569da5e6..bc7a20d5d 100644
--- a/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
+++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData.xcdatamodel/contents
@@ -70,8 +70,9 @@
-
+
+
@@ -223,7 +224,7 @@
-
+
diff --git a/CoreDataStack/Entity/Notification.swift b/CoreDataStack/Entity/Notification.swift
index 8a0595f6c..31c361aa4 100644
--- a/CoreDataStack/Entity/Notification.swift
+++ b/CoreDataStack/Entity/Notification.swift
@@ -12,13 +12,14 @@ public final class MastodonNotification: NSManagedObject {
public typealias ID = UUID
@NSManaged public private(set) var identifier: ID
@NSManaged public private(set) var id: String
- @NSManaged public private(set) var domain: String
@NSManaged public private(set) var createAt: Date
@NSManaged public private(set) var updatedAt: Date
- @NSManaged public private(set) var type: String
+ @NSManaged public private(set) var typeRaw: String
@NSManaged public private(set) var account: MastodonUser
@NSManaged public private(set) var status: Status?
+ @NSManaged public private(set) var domain: String
+ @NSManaged public private(set) var userID: String
}
extension MastodonNotification {
@@ -26,12 +27,6 @@ extension MastodonNotification {
super.awakeFromInsert()
setPrimitiveValue(UUID(), forKey: #keyPath(MastodonNotification.identifier))
}
-
- public override func willSave() {
- super.willSave()
- setPrimitiveValue(Date(), forKey: #keyPath(MastodonNotification.updatedAt))
- }
-
}
public extension MastodonNotification {
@@ -39,16 +34,19 @@ public extension MastodonNotification {
static func insert(
into context: NSManagedObjectContext,
domain: String,
+ userID: String,
+ networkDate: Date,
property: Property
) -> MastodonNotification {
let notification: MastodonNotification = context.insertObject()
notification.id = property.id
notification.createAt = property.createdAt
- notification.updatedAt = property.createdAt
- notification.type = property.type
+ notification.updatedAt = networkDate
+ notification.typeRaw = property.typeRaw
notification.account = property.account
notification.status = property.status
notification.domain = domain
+ notification.userID = userID
return notification
}
}
@@ -56,19 +54,20 @@ public extension MastodonNotification {
public extension MastodonNotification {
struct Property {
public init(id: String,
- type: String,
+ typeRaw: String,
account: MastodonUser,
status: Status?,
- createdAt: Date) {
+ createdAt: Date
+ ) {
self.id = id
- self.type = type
+ self.typeRaw = typeRaw
self.account = account
self.status = status
self.createdAt = createdAt
}
public let id: String
- public let type: String
+ public let typeRaw: String
public let account: MastodonUser
public let status: Status?
public let createdAt: Date
@@ -76,19 +75,31 @@ public extension MastodonNotification {
}
extension MastodonNotification {
- public static func predicate(domain: String) -> NSPredicate {
+ static func predicate(domain: String) -> NSPredicate {
return NSPredicate(format: "%K == %@", #keyPath(MastodonNotification.domain), domain)
}
- static func predicate(type: String) -> NSPredicate {
- return NSPredicate(format: "%K == %@", #keyPath(MastodonNotification.type), type)
+ static func predicate(userID: String) -> NSPredicate {
+ return NSPredicate(format: "%K == %@", #keyPath(MastodonNotification.userID), userID)
}
- public static func predicate(domain: String, type: String) -> NSPredicate {
- return NSCompoundPredicate(andPredicateWithSubpredicates: [
- MastodonNotification.predicate(domain: domain),
- MastodonNotification.predicate(type: type)
- ])
+ static func predicate(typeRaw: String) -> NSPredicate {
+ return NSPredicate(format: "%K == %@", #keyPath(MastodonNotification.typeRaw), typeRaw)
+ }
+
+ public static func predicate(domain: String, userID: String, typeRaw: String? = nil) -> NSPredicate {
+ if let typeRaw = typeRaw {
+ return NSCompoundPredicate(andPredicateWithSubpredicates: [
+ MastodonNotification.predicate(domain: domain),
+ MastodonNotification.predicate(typeRaw: typeRaw),
+ MastodonNotification.predicate(userID: userID),
+ ])
+ } else {
+ return NSCompoundPredicate(andPredicateWithSubpredicates: [
+ MastodonNotification.predicate(domain: domain),
+ MastodonNotification.predicate(userID: userID)
+ ])
+ }
}
}
diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj
index d088d3176..3244dcd33 100644
--- a/Mastodon.xcodeproj/project.pbxproj
+++ b/Mastodon.xcodeproj/project.pbxproj
@@ -37,7 +37,6 @@
2D152A9225C2980C009AA50C /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D152A9125C2980C009AA50C /* UIFont.swift */; };
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; };
- 2D19864F261C372A00F0B013 /* CommonBottomLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D19864E261C372A00F0B013 /* CommonBottomLoader.swift */; };
2D198655261C3C4300F0B013 /* SearchViewModel+LoadOldestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198654261C3C4300F0B013 /* SearchViewModel+LoadOldestState.swift */; };
2D206B7225F5D27F00143C56 /* AudioContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7125F5D27F00143C56 /* AudioContainerView.swift */; };
2D206B8025F5F45E00143C56 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B7F25F5F45E00143C56 /* UIImage.swift */; };
@@ -427,7 +426,6 @@
2D152A9125C2980C009AA50C /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; };
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = ""; };
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = ""; };
- 2D19864E261C372A00F0B013 /* CommonBottomLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonBottomLoader.swift; sourceTree = ""; };
2D198654261C3C4300F0B013 /* SearchViewModel+LoadOldestState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewModel+LoadOldestState.swift"; sourceTree = ""; };
2D206B7125F5D27F00143C56 /* AudioContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioContainerView.swift; sourceTree = ""; };
2D206B7F25F5F45E00143C56 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; };
@@ -1154,7 +1152,6 @@
isa = PBXGroup;
children = (
2DFAD5362617010500F9EE7C /* SearchingTableViewCell.swift */,
- 2D19864E261C372A00F0B013 /* CommonBottomLoader.swift */,
);
path = TableViewCell;
sourceTree = "";
@@ -2272,7 +2269,6 @@
DBB5256E2612D5A1002F1F29 /* ProfileStatusDashboardView.swift in Sources */,
2D24E1232626ED9D00A59D4F /* UIView+Gesture.swift in Sources */,
DB45FAE325CA7181005A8AC7 /* MastodonUser.swift in Sources */,
- 2D19864F261C372A00F0B013 /* CommonBottomLoader.swift in Sources */,
DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */,
0F202213261351F5000C64BF /* APIService+HashtagTimeline.swift in Sources */,
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */,
diff --git a/Mastodon/Diffiable/Item/NotificationItem.swift b/Mastodon/Diffiable/Item/NotificationItem.swift
index c160eac5e..ba0d0c140 100644
--- a/Mastodon/Diffiable/Item/NotificationItem.swift
+++ b/Mastodon/Diffiable/Item/NotificationItem.swift
@@ -17,10 +17,10 @@ enum NotificationItem {
extension NotificationItem: Equatable {
static func == (lhs: NotificationItem, rhs: NotificationItem) -> Bool {
switch (lhs, rhs) {
- case (.bottomLoader, .bottomLoader):
- return true
case (.notification(let idLeft), .notification(let idRight)):
return idLeft == idRight
+ case (.bottomLoader, .bottomLoader):
+ return true
default:
return false
}
diff --git a/Mastodon/Diffiable/Section/NotificationSection.swift b/Mastodon/Diffiable/Section/NotificationSection.swift
index 5e3cd2d9e..0b63bb241 100644
--- a/Mastodon/Diffiable/Section/NotificationSection.swift
+++ b/Mastodon/Diffiable/Section/NotificationSection.swift
@@ -33,7 +33,7 @@ extension NotificationSection {
case .notification(let objectID):
let notification = managedObjectContext.object(with: objectID) as! MastodonNotification
- let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.type)
+ let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw)
let timeText = notification.createAt.shortTimeAgoSinceNow
@@ -128,7 +128,7 @@ extension NotificationSection {
return cell
}
case .bottomLoader:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: CommonBottomLoader.self)) as! CommonBottomLoader
+ let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) as! TimelineBottomLoaderTableViewCell
cell.startAnimating()
return cell
}
diff --git a/Mastodon/Diffiable/Section/SearchResultSection.swift b/Mastodon/Diffiable/Section/SearchResultSection.swift
index e01063c86..1b9230ee0 100644
--- a/Mastodon/Diffiable/Section/SearchResultSection.swift
+++ b/Mastodon/Diffiable/Section/SearchResultSection.swift
@@ -44,7 +44,7 @@ extension SearchResultSection {
cell.config(with: user)
return cell
case .bottomLoader:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: CommonBottomLoader.self)) as! CommonBottomLoader
+ let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) as! TimelineBottomLoaderTableViewCell
cell.startAnimating()
return cell
}
diff --git a/Mastodon/Extension/UIView+Constraint.swift b/Mastodon/Extension/UIView+Constraint.swift
index baa923ada..ded8846d4 100644
--- a/Mastodon/Extension/UIView+Constraint.swift
+++ b/Mastodon/Extension/UIView+Constraint.swift
@@ -174,8 +174,9 @@ extension UIView {
guard superview != nil else { assert(false, "Superview cannot be nil when adding contraints"); return }
translatesAutoresizingMaskIntoConstraints = false
constrain([
- widthAnchor.constraint(equalToConstant: toSize.width),
- heightAnchor.constraint(equalToConstant: toSize.height)])
+ widthAnchor.constraint(equalToConstant: toSize.width).priority(.required - 1),
+ heightAnchor.constraint(equalToConstant: toSize.height).priority(.required - 1)
+ ])
}
func pin(top: CGFloat?,left: CGFloat?,bottom: CGFloat?, right: CGFloat?) {
diff --git a/Mastodon/Scene/Notification/NotificationViewController.swift b/Mastodon/Scene/Notification/NotificationViewController.swift
index becf86771..ddc997a5d 100644
--- a/Mastodon/Scene/Notification/NotificationViewController.swift
+++ b/Mastodon/Scene/Notification/NotificationViewController.swift
@@ -33,7 +33,7 @@ final class NotificationViewController: UIViewController, NeedsDependency {
tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
tableView.register(NotificationTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationTableViewCell.self))
tableView.register(NotificationStatusTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationStatusTableViewCell.self))
- tableView.register(CommonBottomLoader.self, forCellReuseIdentifier: String(describing: CommonBottomLoader.self))
+ tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
tableView.tableFooterView = UIView()
tableView.rowHeight = UITableView.automaticDimension
return tableView
@@ -111,15 +111,15 @@ extension NotificationViewController {
extension NotificationViewController {
@objc private func segmentedControlValueChanged(_ sender: UISegmentedControl) {
os_log("%{public}s[%{public}ld], %{public}s: select at index: %ld", (#file as NSString).lastPathComponent, #line, #function, sender.selectedSegmentIndex)
- guard let domain = viewModel.activeMastodonAuthenticationBox.value?.domain else {
+ guard let domain = viewModel.activeMastodonAuthenticationBox.value?.domain, let userID = viewModel.activeMastodonAuthenticationBox.value?.userID else {
return
}
if sender.selectedSegmentIndex == 0 {
- viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain)
+ viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain, userID: userID)
} else {
- viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain, type: Mastodon.Entity.Notification.NotificationType.mention.rawValue)
+ viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain,userID: userID, typeRaw: Mastodon.Entity.Notification.NotificationType.mention.rawValue)
}
- viewModel.selectedIndex.value = sender.selectedSegmentIndex
+ viewModel.selectedIndex.value = NotificationViewModel.NotificationSegment.init(rawValue: sender.selectedSegmentIndex)!
}
@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {
@@ -196,7 +196,7 @@ extension NotificationViewController {
}
extension NotificationViewController: LoadMoreConfigurableTableViewContainer {
- typealias BottomLoaderTableViewCell = CommonBottomLoader
+ typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
typealias LoadingState = NotificationViewModel.LoadOldestState.Loading
var loadMoreConfigurableTableView: UITableView { tableView }
var loadMoreConfigurableStateMachine: GKStateMachine { viewModel.loadoldestStateMachine }
diff --git a/Mastodon/Scene/Notification/NotificationViewModel+LoadLatestState.swift b/Mastodon/Scene/Notification/NotificationViewModel+LoadLatestState.swift
index 38f24c586..0e6b0d622 100644
--- a/Mastodon/Scene/Notification/NotificationViewModel+LoadLatestState.swift
+++ b/Mastodon/Scene/Notification/NotificationViewModel+LoadLatestState.swift
@@ -53,12 +53,14 @@ extension NotificationViewModel.LoadLatestState {
sinceID: nil,
minID: nil,
limit: nil,
- excludeTypes: Mastodon.API.Notifications.allExcludeTypes(),
- accountID: nil)
+ excludeTypes: [.followRequest],
+ accountID: nil
+ )
viewModel.context.apiService.allNotifications(
domain: activeMastodonAuthenticationBox.domain,
query: query,
- mastodonAuthenticationBox: activeMastodonAuthenticationBox)
+ mastodonAuthenticationBox: activeMastodonAuthenticationBox
+ )
.sink { completion in
switch completion {
case .failure(let error):
diff --git a/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift b/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift
index a26dedeeb..8075ce375 100644
--- a/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift
+++ b/Mastodon/Scene/Notification/NotificationViewModel+LoadOldestState.swift
@@ -50,7 +50,7 @@ extension NotificationViewModel.LoadOldestState {
}
let notifications: [MastodonNotification]? = {
let request = MastodonNotification.sortedFetchRequest
- request.predicate = MastodonNotification.predicate(domain: activeMastodonAuthenticationBox.domain)
+ request.predicate = MastodonNotification.predicate(domain: activeMastodonAuthenticationBox.domain, userID: activeMastodonAuthenticationBox.userID)
request.returnsObjectsAsFaults = false
do {
return try self.viewModel?.context.managedObjectContext.fetch(request)
@@ -71,12 +71,13 @@ extension NotificationViewModel.LoadOldestState {
sinceID: nil,
minID: nil,
limit: nil,
- excludeTypes: Mastodon.API.Notifications.allExcludeTypes(),
+ excludeTypes: [.followRequest],
accountID: nil)
viewModel.context.apiService.allNotifications(
domain: activeMastodonAuthenticationBox.domain,
query: query,
- mastodonAuthenticationBox: activeMastodonAuthenticationBox)
+ mastodonAuthenticationBox: activeMastodonAuthenticationBox
+ )
.sink { completion in
switch completion {
case .failure(let error):
@@ -89,16 +90,17 @@ extension NotificationViewModel.LoadOldestState {
stateMachine.enter(Idle.self)
} receiveValue: { [weak viewModel] response in
guard let viewModel = viewModel else { return }
- if viewModel.selectedIndex.value == 1 {
- viewModel.noMoreNotification.value = response.value.isEmpty
- let list = response.value.filter { $0.type == Mastodon.Entity.Notification.NotificationType.mention }
- if list.isEmpty {
+ switch viewModel.selectedIndex.value {
+ case .EveryThing:
+ if response.value.isEmpty {
stateMachine.enter(NoMore.self)
} else {
stateMachine.enter(Idle.self)
}
- } else {
- if response.value.isEmpty {
+ case .Mentions:
+ viewModel.noMoreNotification.value = response.value.isEmpty
+ let list = response.value.filter { $0.type == Mastodon.Entity.Notification.NotificationType.mention }
+ if list.isEmpty {
stateMachine.enter(NoMore.self)
} else {
stateMachine.enter(Idle.self)
diff --git a/Mastodon/Scene/Notification/NotificationViewModel.swift b/Mastodon/Scene/Notification/NotificationViewModel.swift
index f64c07fc9..e026af732 100644
--- a/Mastodon/Scene/Notification/NotificationViewModel.swift
+++ b/Mastodon/Scene/Notification/NotificationViewModel.swift
@@ -22,7 +22,7 @@ final class NotificationViewModel: NSObject {
weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
let viewDidLoad = PassthroughSubject()
- let selectedIndex = CurrentValueSubject(0)
+ let selectedIndex = CurrentValueSubject(.EveryThing)
let noMoreNotification = CurrentValueSubject(false)
let activeMastodonAuthenticationBox: CurrentValueSubject
@@ -88,8 +88,8 @@ final class NotificationViewModel: NSObject {
.sink(receiveValue: { [weak self] box in
guard let self = self else { return }
self.activeMastodonAuthenticationBox.value = box
- if let domain = box?.domain {
- self.notificationPredicate.value = MastodonNotification.predicate(domain: domain)
+ if let domain = box?.domain, let userID = box?.userID {
+ self.notificationPredicate.value = MastodonNotification.predicate(domain: domain, userID: userID)
}
})
.store(in: &disposeBag)
@@ -115,9 +115,16 @@ final class NotificationViewModel: NSObject {
viewDidLoad
.sink { [weak self] in
- guard let domain = self?.activeMastodonAuthenticationBox.value?.domain else { return }
- self?.notificationPredicate.value = MastodonNotification.predicate(domain: domain)
+ guard let domain = self?.activeMastodonAuthenticationBox.value?.domain, let userID = self?.activeMastodonAuthenticationBox.value?.userID else { return }
+ self?.notificationPredicate.value = MastodonNotification.predicate(domain: domain, userID: userID)
}
.store(in: &disposeBag)
}
}
+
+extension NotificationViewModel {
+ enum NotificationSegment: Int {
+ case EveryThing
+ case Mentions
+ }
+}
diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift
index 6bea35ead..dc3f49bb0 100644
--- a/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift
+++ b/Mastodon/Scene/Notification/TableViewCell/NotificationStatusTableViewCell.swift
@@ -103,7 +103,6 @@ final class NotificationStatusTableViewCell: UITableViewCell {
extension NotificationStatusTableViewCell {
func configure() {
- selectionStyle = .none
let container = UIView()
container.backgroundColor = .clear
@@ -117,11 +116,11 @@ extension NotificationStatusTableViewCell {
container.addSubview(avatatImageView)
avatatImageView.pin(toSize: CGSize(width: 35, height: 35))
- avatatImageView.pin(top: 12, left: 12, bottom: nil, right: nil)
+ avatatImageView.pin(top: 12, left: 0, bottom: nil, right: nil)
container.addSubview(actionImageBackground)
actionImageBackground.pin(toSize: CGSize(width: 24 + NotificationTableViewCell.actionImageBorderWidth, height: 24 + NotificationTableViewCell.actionImageBorderWidth))
- actionImageBackground.pin(top: 33, left: 33, bottom: nil, right: nil)
+ actionImageBackground.pin(top: 33, left: 21, bottom: nil, right: nil)
actionImageBackground.addSubview(actionImageView)
actionImageView.constrainToCenter()
@@ -130,22 +129,21 @@ extension NotificationStatusTableViewCell {
nameLabel.constrain([
nameLabel.topAnchor.constraint(equalTo: container.topAnchor, constant: 12),
nameLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 61)
-
])
container.addSubview(actionLabel)
actionLabel.constrain([
actionLabel.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 4),
actionLabel.centerYAnchor.constraint(equalTo: nameLabel.centerYAnchor),
- container.trailingAnchor.constraint(greaterThanOrEqualTo: actionLabel.trailingAnchor, constant: 4).priority(.defaultLow)
+ container.trailingAnchor.constraint(greaterThanOrEqualTo: actionLabel.trailingAnchor, constant: 4)
])
statusView.contentWarningBlurContentImageView.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color
statusView.isUserInteractionEnabled = false
// remove item don't display
- statusView.actionToolbarContainer.removeFromSuperview()
- statusView.avatarView.removeFromSuperview()
- statusView.usernameLabel.removeFromSuperview()
+ statusView.actionToolbarContainer.isHidden = true
+ statusView.avatarView.isHidden = true
+ statusView.usernameLabel.isHidden = true
container.addSubview(statusBorder)
statusBorder.pin(top: 40, left: 63, bottom: 14, right: 14)
diff --git a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift
index 238d9c67f..cda4d75d7 100644
--- a/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift
+++ b/Mastodon/Scene/Notification/TableViewCell/NotificationTableViewCell.swift
@@ -85,7 +85,6 @@ final class NotificationTableViewCell: UITableViewCell {
extension NotificationTableViewCell {
func configure() {
- selectionStyle = .none
let container = UIView()
container.backgroundColor = .clear
@@ -99,7 +98,7 @@ extension NotificationTableViewCell {
container.addSubview(avatatImageView)
avatatImageView.pin(toSize: CGSize(width: 35, height: 35))
- avatatImageView.pin(top: 12, left: 12, bottom: nil, right: nil)
+ avatatImageView.pin(top: 12, left: 0, bottom: nil, right: nil)
container.addSubview(actionImageBackground)
actionImageBackground.pin(toSize: CGSize(width: 24 + NotificationTableViewCell.actionImageBorderWidth, height: 24 + NotificationTableViewCell.actionImageBorderWidth))
@@ -119,7 +118,7 @@ extension NotificationTableViewCell {
actionLabel.constrain([
actionLabel.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 4),
actionLabel.centerYAnchor.constraint(equalTo: nameLabel.centerYAnchor),
- container.trailingAnchor.constraint(greaterThanOrEqualTo: actionLabel.trailingAnchor, constant: 4).priority(.defaultLow)
+ container.trailingAnchor.constraint(greaterThanOrEqualTo: actionLabel.trailingAnchor, constant: 4)
])
}
diff --git a/Mastodon/Scene/Search/SearchViewController+Searching.swift b/Mastodon/Scene/Search/SearchViewController+Searching.swift
index 43e5d397c..86a27e03d 100644
--- a/Mastodon/Scene/Search/SearchViewController+Searching.swift
+++ b/Mastodon/Scene/Search/SearchViewController+Searching.swift
@@ -17,7 +17,7 @@ extension SearchViewController {
func setupSearchingTableView() {
searchingTableView.delegate = self
searchingTableView.register(SearchingTableViewCell.self, forCellReuseIdentifier: String(describing: SearchingTableViewCell.self))
- searchingTableView.register(CommonBottomLoader.self, forCellReuseIdentifier: String(describing: CommonBottomLoader.self))
+ searchingTableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
view.addSubview(searchingTableView)
searchingTableView.constrain([
searchingTableView.frameLayoutGuide.topAnchor.constraint(equalTo: searchBar.bottomAnchor),
diff --git a/Mastodon/Scene/Search/SearchViewController.swift b/Mastodon/Scene/Search/SearchViewController.swift
index dc9414585..4fee226bf 100644
--- a/Mastodon/Scene/Search/SearchViewController.swift
+++ b/Mastodon/Scene/Search/SearchViewController.swift
@@ -227,7 +227,7 @@ extension SearchViewController: UISearchBarDelegate {
}
extension SearchViewController: LoadMoreConfigurableTableViewContainer {
- typealias BottomLoaderTableViewCell = CommonBottomLoader
+ typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
typealias LoadingState = SearchViewModel.LoadOldestState.Loading
var loadMoreConfigurableTableView: UITableView { searchingTableView }
var loadMoreConfigurableStateMachine: GKStateMachine { viewModel.loadoldestStateMachine }
diff --git a/Mastodon/Scene/Search/TableViewCell/CommonBottomLoader.swift b/Mastodon/Scene/Search/TableViewCell/CommonBottomLoader.swift
deleted file mode 100644
index 2d529972e..000000000
--- a/Mastodon/Scene/Search/TableViewCell/CommonBottomLoader.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// CommonBottomLoader.swift
-// Mastodon
-//
-// Created by sxiaojian on 2021/4/6.
-//
-
-import Foundation
-import UIKit
-
-final class CommonBottomLoader: UITableViewCell {
- let activityIndicatorView: UIActivityIndicatorView = {
- let activityIndicatorView = UIActivityIndicatorView(style: .medium)
- activityIndicatorView.tintColor = Asset.Colors.Label.primary.color
- activityIndicatorView.hidesWhenStopped = true
- return activityIndicatorView
- }()
-
- override func prepareForReuse() {
- super.prepareForReuse()
- }
-
- override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
- super.init(style: style, reuseIdentifier: reuseIdentifier)
- _init()
- }
-
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- _init()
- }
-
- func startAnimating() {
- activityIndicatorView.startAnimating()
- }
-
- func stopAnimating() {
- activityIndicatorView.stopAnimating()
- }
-
- func _init() {
- selectionStyle = .none
- backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
- contentView.addSubview(activityIndicatorView)
- activityIndicatorView.constrainToCenter()
- }
-}
diff --git a/Mastodon/Service/APIService/APIService+Notification.swift b/Mastodon/Service/APIService/APIService+Notification.swift
index e2b90ffd7..ee8f5186c 100644
--- a/Mastodon/Service/APIService/APIService+Notification.swift
+++ b/Mastodon/Service/APIService/APIService+Notification.swift
@@ -16,48 +16,52 @@ extension APIService {
func allNotifications(
domain: String,
query: Mastodon.API.Notifications.Query,
- mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox) -> AnyPublisher, Error>
- {
+ mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
+ ) -> AnyPublisher, Error> {
let authorization = mastodonAuthenticationBox.userAuthorization
+ let userID = mastodonAuthenticationBox.userID
return Mastodon.API.Notifications.getNotifications(
session: session,
domain: domain,
query: query,
- authorization: authorization)
- .flatMap { response -> AnyPublisher, Error> in
- let log = OSLog.api
- return self.backgroundManagedObjectContext.performChanges {
- response.value.forEach { notification in
- let (mastodonUser, _) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: notification.account, userCache: nil, networkDate: Date(), log: log)
- var status: Status?
- if let statusEntity = notification.status {
- let (statusInCoreData, _, _) = APIService.CoreData.createOrMergeStatus(
- into: self.backgroundManagedObjectContext,
- for: nil,
- domain: domain,
- entity: statusEntity,
- statusCache: nil,
- userCache: nil,
- networkDate: Date(),
- log: log)
- status = statusInCoreData
- }
- // use constrain to avoid repeated save
- let notification = MastodonNotification.insert(into: self.backgroundManagedObjectContext, domain: domain, property: MastodonNotification.Property(id: notification.id, type: notification.type.rawValue, account: mastodonUser, status: status, createdAt: notification.createdAt))
- os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)", (#file as NSString).lastPathComponent, #line, #function, notification.type, notification.account.username)
+ authorization: authorization
+ )
+ .flatMap { response -> AnyPublisher, Error> in
+ let log = OSLog.api
+ return self.backgroundManagedObjectContext.performChanges {
+ response.value.forEach { notification in
+ let (mastodonUser, _) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: notification.account, userCache: nil, networkDate: Date(), log: log)
+ var status: Status?
+ if let statusEntity = notification.status {
+ let (statusInCoreData, _, _) = APIService.CoreData.createOrMergeStatus(
+ into: self.backgroundManagedObjectContext,
+ for: nil,
+ domain: domain,
+ entity: statusEntity,
+ statusCache: nil,
+ userCache: nil,
+ networkDate: Date(),
+ log: log
+ )
+ status = statusInCoreData
}
+ // use constrain to avoid repeated save
+ let property = MastodonNotification.Property(id: notification.id, typeRaw: notification.type.rawValue, account: mastodonUser, status: status, createdAt: notification.createdAt)
+ let notification = MastodonNotification.insert(into: self.backgroundManagedObjectContext, domain: domain, userID: userID, networkDate: response.networkDate, property: property)
+ os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)", (#file as NSString).lastPathComponent, #line, #function, notification.typeRaw, notification.account.username)
}
- .setFailureType(to: Error.self)
- .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Notification]> in
- switch result {
- case .success:
- return response
- case .failure(let error):
- throw error
- }
+ }
+ .setFailureType(to: Error.self)
+ .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Notification]> in
+ switch result {
+ case .success:
+ return response
+ case .failure(let error):
+ throw error
}
- .eraseToAnyPublisher()
}
.eraseToAnyPublisher()
+ }
+ .eraseToAnyPublisher()
}
}
diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Notifications.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Notifications.swift
index 1cc54add5..b0ab13edb 100644
--- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Notifications.swift
+++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Notifications.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-public extension Mastodon.API.Notifications {
+extension Mastodon.API.Notifications {
internal static func notificationsEndpointURL(domain: String) -> URL {
Mastodon.API.endpointURL(domain: domain).appendingPathComponent("notifications")
}
@@ -31,7 +31,7 @@ public extension Mastodon.API.Notifications {
/// - query: `NotificationsQuery` with query parameters
/// - authorization: User token
/// - Returns: `AnyPublisher` contains `Token` nested in the response
- static func getNotifications(
+ public static func getNotifications(
session: URLSession,
domain: String,
query: Mastodon.API.Notifications.Query,
@@ -64,7 +64,7 @@ public extension Mastodon.API.Notifications {
/// - notificationID: ID of the notification.
/// - authorization: User token
/// - Returns: `AnyPublisher` contains `Token` nested in the response
- static func getNotification(
+ public static func getNotification(
session: URLSession,
domain: String,
notificationID: String,
@@ -82,18 +82,10 @@ public extension Mastodon.API.Notifications {
}
.eraseToAnyPublisher()
}
-
- static func allExcludeTypes() -> [Mastodon.Entity.Notification.NotificationType] {
- [.followRequest]
- }
-
- static func mentionsExcludeTypes() -> [Mastodon.Entity.Notification.NotificationType] {
- [.follow, .followRequest, .favourite, .reblog, .poll]
- }
}
-public extension Mastodon.API.Notifications {
- struct Query: Codable, PagedQueryType, GetQuery {
+extension Mastodon.API.Notifications {
+ public struct Query: PagedQueryType, GetQuery {
public let maxID: Mastodon.Entity.Status.ID?
public let sinceID: Mastodon.Entity.Status.ID?
public let minID: Mastodon.Entity.Status.ID?