chore: code format

This commit is contained in:
sunxiaojian 2021-04-16 13:45:54 +08:00
parent ecf622b866
commit ca7eb7bb12
18 changed files with 130 additions and 164 deletions

View File

@ -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"/>

View File

@ -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)
])
}
}
}

View File

@ -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 */,

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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?) {

View File

@ -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 }

View File

@ -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):

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)
])
}

View File

@ -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),

View File

@ -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 }

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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?