chore: code format
This commit is contained in:
parent
ecf622b866
commit
ca7eb7bb12
|
@ -70,8 +70,9 @@
|
|||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="type" attributeType="String"/>
|
||||
<attribute name="typeRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status"/>
|
||||
<uniquenessConstraints>
|
||||
|
@ -223,7 +224,7 @@
|
|||
<element name="History" positionX="0" positionY="0" width="128" height="119"/>
|
||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
||||
<element name="MastodonAuthentication" positionX="0" positionY="0" width="128" height="209"/>
|
||||
<element name="MastodonNotification" positionX="9" positionY="162" width="128" height="149"/>
|
||||
<element name="MastodonNotification" positionX="9" positionY="162" width="128" height="164"/>
|
||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="659"/>
|
||||
<element name="Mention" positionX="0" positionY="0" width="128" height="134"/>
|
||||
<element name="Poll" positionX="0" positionY="0" width="128" height="194"/>
|
||||
|
|
|
@ -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)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = "<group>"; };
|
||||
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = "<group>"; };
|
||||
2D19864E261C372A00F0B013 /* CommonBottomLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonBottomLoader.swift; sourceTree = "<group>"; };
|
||||
2D198654261C3C4300F0B013 /* SearchViewModel+LoadOldestState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewModel+LoadOldestState.swift"; sourceTree = "<group>"; };
|
||||
2D206B7125F5D27F00143C56 /* AudioContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioContainerView.swift; sourceTree = "<group>"; };
|
||||
2D206B7F25F5F45E00143C56 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
|
||||
|
@ -1154,7 +1152,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
2DFAD5362617010500F9EE7C /* SearchingTableViewCell.swift */,
|
||||
2D19864E261C372A00F0B013 /* CommonBottomLoader.swift */,
|
||||
);
|
||||
path = TableViewCell;
|
||||
sourceTree = "<group>";
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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?) {
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -22,7 +22,7 @@ final class NotificationViewModel: NSObject {
|
|||
weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
|
||||
|
||||
let viewDidLoad = PassthroughSubject<Void, Never>()
|
||||
let selectedIndex = CurrentValueSubject<Int, Never>(0)
|
||||
let selectedIndex = CurrentValueSubject<NotificationSegment, Never>(.EveryThing)
|
||||
let noMoreNotification = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
let activeMastodonAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -16,48 +16,52 @@ extension APIService {
|
|||
func allNotifications(
|
||||
domain: String,
|
||||
query: Mastodon.API.Notifications.Query,
|
||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Notification]>, Error>
|
||||
{
|
||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
|
||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Notification]>, 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<Mastodon.Response.Content<[Mastodon.Entity.Notification]>, 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<Mastodon.Response.Content<[Mastodon.Entity.Notification]>, 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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
Loading…
Reference in New Issue