commit
6481565701
|
@ -102,6 +102,10 @@ extension MastodonNotification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func predicate(validTypesRaws types: [String]) -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K IN %@", #keyPath(MastodonNotification.typeRaw), types)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonNotification: Managed {
|
extension MastodonNotification: Managed {
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
5DDDF1932617442700311060 /* Mastodon+Entity+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */; };
|
5DDDF1932617442700311060 /* Mastodon+Entity+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */; };
|
||||||
5DDDF1992617447F00311060 /* Mastodon+Entity+Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */; };
|
5DDDF1992617447F00311060 /* Mastodon+Entity+Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */; };
|
||||||
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */; };
|
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */; };
|
||||||
5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */; };
|
5DF1054125F886D400D6C0D4 /* VideoPlaybackService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054025F886D400D6C0D4 /* VideoPlaybackService.swift */; };
|
||||||
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */; };
|
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */; };
|
||||||
5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; };
|
5DF1056425F887CB00D6C0D4 /* AVPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */; };
|
||||||
5DF1057925F88A1D00D6C0D4 /* PlayerContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */; };
|
5DF1057925F88A1D00D6C0D4 /* PlayerContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */; };
|
||||||
|
@ -732,7 +732,7 @@
|
||||||
5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Account.swift"; sourceTree = "<group>"; };
|
5DDDF1922617442700311060 /* Mastodon+Entity+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Account.swift"; sourceTree = "<group>"; };
|
||||||
5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Tag.swift"; sourceTree = "<group>"; };
|
5DDDF1982617447F00311060 /* Mastodon+Entity+Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Tag.swift"; sourceTree = "<group>"; };
|
||||||
5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+History.swift"; sourceTree = "<group>"; };
|
5DDDF1A82617489F00311060 /* Mastodon+Entity+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+History.swift"; sourceTree = "<group>"; };
|
||||||
5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViedeoPlaybackService.swift; sourceTree = "<group>"; };
|
5DF1054025F886D400D6C0D4 /* VideoPlaybackService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlaybackService.swift; sourceTree = "<group>"; };
|
||||||
5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; };
|
5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = "<group>"; };
|
||||||
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; };
|
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayer.swift; sourceTree = "<group>"; };
|
||||||
5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerContainerView.swift; sourceTree = "<group>"; };
|
5DF1057825F88A1D00D6C0D4 /* PlayerContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerContainerView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1343,7 +1343,7 @@
|
||||||
DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */,
|
DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */,
|
||||||
2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */,
|
2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */,
|
||||||
2DA6054625F716A2006356F9 /* PlaybackState.swift */,
|
2DA6054625F716A2006356F9 /* PlaybackState.swift */,
|
||||||
5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */,
|
5DF1054025F886D400D6C0D4 /* VideoPlaybackService.swift */,
|
||||||
DB71FD4B25F8C80E00512AE1 /* StatusPrefetchingService.swift */,
|
DB71FD4B25F8C80E00512AE1 /* StatusPrefetchingService.swift */,
|
||||||
DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */,
|
DBC7A67B260DFADE00E57475 /* StatusPublishService.swift */,
|
||||||
2D9DB966263A76FB007C1D71 /* BlockDomainService.swift */,
|
2D9DB966263A76FB007C1D71 /* BlockDomainService.swift */,
|
||||||
|
@ -2947,7 +2947,7 @@
|
||||||
DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */,
|
DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */,
|
||||||
DBCC3B9526157E6E0045B23D /* APIService+Relationship.swift in Sources */,
|
DBCC3B9526157E6E0045B23D /* APIService+Relationship.swift in Sources */,
|
||||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||||
5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */,
|
5DF1054125F886D400D6C0D4 /* VideoPlaybackService.swift in Sources */,
|
||||||
DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */,
|
DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */,
|
||||||
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */,
|
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */,
|
||||||
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
2D61335E25C1894B00CAE157 /* APIService.swift in Sources */,
|
||||||
|
@ -3614,7 +3614,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 10;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -3622,7 +3622,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.6.0;
|
MARKETING_VERSION = 0.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -3641,7 +3641,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 10;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -3649,7 +3649,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.6.0;
|
MARKETING_VERSION = 0.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -3904,7 +3904,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 10;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -3912,7 +3912,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.6.0;
|
MARKETING_VERSION = 0.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -3927,7 +3927,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 10;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -3935,7 +3935,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.6.0;
|
MARKETING_VERSION = 0.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
|
|
@ -30,13 +30,15 @@ extension NotificationSection {
|
||||||
guard let dependency = dependency else { return nil }
|
guard let dependency = dependency else { return nil }
|
||||||
switch notificationItem {
|
switch notificationItem {
|
||||||
case .notification(let objectID, let attribute):
|
case .notification(let objectID, let attribute):
|
||||||
|
|
||||||
let notification = managedObjectContext.object(with: objectID) as! MastodonNotification
|
let notification = managedObjectContext.object(with: objectID) as! MastodonNotification
|
||||||
guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else {
|
guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else {
|
||||||
|
// filter out invalid type using predicate
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
return nil
|
return UITableViewCell()
|
||||||
}
|
}
|
||||||
let timeText = notification.createAt.slowedTimeAgoSinceNow
|
|
||||||
|
let createAt = notification.createAt
|
||||||
|
let timeText = createAt.slowedTimeAgoSinceNow
|
||||||
|
|
||||||
let actionText = type.actionText
|
let actionText = type.actionText
|
||||||
let actionImageName = type.actionImageName
|
let actionImageName = type.actionImageName
|
||||||
|
@ -57,23 +59,24 @@ extension NotificationSection {
|
||||||
requestUserID: requestUserID,
|
requestUserID: requestUserID,
|
||||||
statusItemAttribute: attribute
|
statusItemAttribute: attribute
|
||||||
)
|
)
|
||||||
|
cell.actionImageBackground.backgroundColor = color
|
||||||
|
cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName
|
||||||
|
cell.actionLabel.text = actionText + " · " + timeText
|
||||||
timestampUpdatePublisher
|
timestampUpdatePublisher
|
||||||
.sink { _ in
|
.sink { [weak cell] _ in
|
||||||
let timeText = notification.createAt.slowedTimeAgoSinceNow
|
guard let cell = cell else { return }
|
||||||
|
let timeText = createAt.slowedTimeAgoSinceNow
|
||||||
cell.actionLabel.text = actionText + " · " + timeText
|
cell.actionLabel.text = actionText + " · " + timeText
|
||||||
}
|
}
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
cell.actionImageBackground.backgroundColor = color
|
|
||||||
cell.actionLabel.text = actionText + " · " + timeText
|
|
||||||
cell.nameLabel.text = notification.account.displayName.isEmpty ? notification.account.username : notification.account.displayName
|
|
||||||
if let url = notification.account.avatarImageURL() {
|
if let url = notification.account.avatarImageURL() {
|
||||||
cell.avatatImageView.af.setImage(
|
cell.avatarImageView.af.setImage(
|
||||||
withURL: url,
|
withURL: url,
|
||||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||||
imageTransition: .crossDissolve(0.2)
|
imageTransition: .crossDissolve(0.2)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
cell.avatatImageView.gesture().sink { [weak cell] _ in
|
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||||
cell?.delegate?.userAvatarDidPressed(notification: notification)
|
cell?.delegate?.userAvatarDidPressed(notification: notification)
|
||||||
}
|
}
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
|
@ -86,8 +89,9 @@ extension NotificationSection {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell
|
||||||
cell.delegate = delegate
|
cell.delegate = delegate
|
||||||
timestampUpdatePublisher
|
timestampUpdatePublisher
|
||||||
.sink { _ in
|
.sink { [weak cell] _ in
|
||||||
let timeText = notification.createAt.slowedTimeAgoSinceNow
|
guard let cell = cell else { return }
|
||||||
|
let timeText = createAt.slowedTimeAgoSinceNow
|
||||||
cell.actionLabel.text = actionText + " · " + timeText
|
cell.actionLabel.text = actionText + " · " + timeText
|
||||||
}
|
}
|
||||||
.store(in: &cell.disposeBag)
|
.store(in: &cell.disposeBag)
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol EmojiContinaer {
|
protocol EmojiContainer {
|
||||||
var emojisData: Data? { get }
|
var emojisData: Data? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EmojiContinaer {
|
extension EmojiContainer {
|
||||||
|
|
||||||
static func encode(emojis: [Mastodon.Entity.Emoji]) -> Data? {
|
static func encode(emojis: [Mastodon.Entity.Emoji]) -> Data? {
|
||||||
return try? JSONEncoder().encode(emojis)
|
return try? JSONEncoder().encode(emojis)
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol FieldContinaer {
|
protocol FieldContainer {
|
||||||
var fieldsData: Data? { get }
|
var fieldsData: Data? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FieldContinaer {
|
extension FieldContainer {
|
||||||
|
|
||||||
static func encode(fields: [Mastodon.Entity.Field]) -> Data? {
|
static func encode(fields: [Mastodon.Entity.Field]) -> Data? {
|
||||||
return try? JSONEncoder().encode(fields)
|
return try? JSONEncoder().encode(fields)
|
||||||
|
|
|
@ -101,5 +101,5 @@ extension MastodonUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonUser: EmojiContinaer { }
|
extension MastodonUser: EmojiContainer { }
|
||||||
extension MastodonUser: FieldContinaer { }
|
extension MastodonUser: FieldContainer { }
|
||||||
|
|
|
@ -88,4 +88,4 @@ extension Status {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Status: EmojiContinaer { }
|
extension Status: EmojiContainer { }
|
||||||
|
|
|
@ -17,7 +17,6 @@ enum MastodonStatusContent {
|
||||||
static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult {
|
static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult {
|
||||||
let document: String = {
|
let document: String = {
|
||||||
var content = content
|
var content = content
|
||||||
content = content.replacingOccurrences(of: "<br/>", with: "\n")
|
|
||||||
for (shortcode, url) in emojiDict {
|
for (shortcode, url) in emojiDict {
|
||||||
let emojiNode = "<span class=\"emoji\" href=\"\(url.absoluteString)\">\(shortcode)</span>"
|
let emojiNode = "<span class=\"emoji\" href=\"\(url.absoluteString)\">\(shortcode)</span>"
|
||||||
let pattern = ":\(shortcode):"
|
let pattern = ":\(shortcode):"
|
||||||
|
@ -189,6 +188,14 @@ extension MastodonStatusContent {
|
||||||
|
|
||||||
static func parse(document: String) throws -> MastodonStatusContent.Node {
|
static func parse(document: String) throws -> MastodonStatusContent.Node {
|
||||||
let html = try HTML(html: document, encoding: .utf8)
|
let html = try HTML(html: document, encoding: .utf8)
|
||||||
|
|
||||||
|
// add `\r\n` explicit due to Kanna text missing it after convert to text
|
||||||
|
// ref: https://github.com/tid-kijyun/Kanna/issues/150
|
||||||
|
let brNodes = html.css("br").makeIterator()
|
||||||
|
while let brNode = brNodes.next() {
|
||||||
|
brNode.addNextSibling(try! HTML(html: "<span>\r\n</span>", encoding: .utf8).body!)
|
||||||
|
}
|
||||||
|
|
||||||
let body = html.body ?? nil
|
let body = html.body ?? nil
|
||||||
let text = body?.text ?? ""
|
let text = body?.text ?? ""
|
||||||
let level = 0
|
let level = 0
|
||||||
|
|
|
@ -368,6 +368,7 @@ extension ComposeViewController {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
let image = type.image(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
let image = type.image(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
||||||
self.composeToolbarView.visibilityButton.setImage(image, for: .normal)
|
self.composeToolbarView.visibilityButton.setImage(image, for: .normal)
|
||||||
|
self.composeToolbarView.activeVisibilityType.value = type
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
@ -676,7 +677,7 @@ extension ComposeViewController: TextEditorViewTextAttributesDelegate {
|
||||||
updateAttributedString attributedString: NSAttributedString,
|
updateAttributedString attributedString: NSAttributedString,
|
||||||
completion: @escaping (NSAttributedString?) -> Void
|
completion: @escaping (NSAttributedString?) -> Void
|
||||||
) {
|
) {
|
||||||
// FIXME: needs O(1) update completion to fix profermance issue
|
// FIXME: needs O(1) update completion to fix performance issue
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
let string = attributedString.string
|
let string = attributedString.string
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: update: %s", ((#file as NSString).lastPathComponent), #line, #function, string)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: update: %s", ((#file as NSString).lastPathComponent), #line, #function, string)
|
||||||
|
@ -1291,7 +1292,8 @@ extension ComposeViewController {
|
||||||
case togglePoll
|
case togglePoll
|
||||||
case toggleContentWarning
|
case toggleContentWarning
|
||||||
case selectVisibilityPublic
|
case selectVisibilityPublic
|
||||||
case selectVisibilityUnlisted
|
// TODO: remove selectVisibilityUnlisted from codebase
|
||||||
|
// case selectVisibilityUnlisted
|
||||||
case selectVisibilityPrivate
|
case selectVisibilityPrivate
|
||||||
case selectVisibilityDirect
|
case selectVisibilityDirect
|
||||||
|
|
||||||
|
@ -1305,7 +1307,7 @@ extension ComposeViewController {
|
||||||
case .togglePoll: return L10n.Scene.Compose.Keyboard.togglePoll
|
case .togglePoll: return L10n.Scene.Compose.Keyboard.togglePoll
|
||||||
case .toggleContentWarning: return L10n.Scene.Compose.Keyboard.toggleContentWarning
|
case .toggleContentWarning: return L10n.Scene.Compose.Keyboard.toggleContentWarning
|
||||||
case .selectVisibilityPublic: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.public)
|
case .selectVisibilityPublic: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.public)
|
||||||
case .selectVisibilityUnlisted: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.unlisted)
|
// case .selectVisibilityUnlisted: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.unlisted)
|
||||||
case .selectVisibilityPrivate: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.private)
|
case .selectVisibilityPrivate: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.private)
|
||||||
case .selectVisibilityDirect: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.direct)
|
case .selectVisibilityDirect: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.direct)
|
||||||
}
|
}
|
||||||
|
@ -1322,9 +1324,9 @@ extension ComposeViewController {
|
||||||
case .togglePoll: return "p" // + shift + command
|
case .togglePoll: return "p" // + shift + command
|
||||||
case .toggleContentWarning: return "c" // + shift + command
|
case .toggleContentWarning: return "c" // + shift + command
|
||||||
case .selectVisibilityPublic: return "1" // + command
|
case .selectVisibilityPublic: return "1" // + command
|
||||||
case .selectVisibilityUnlisted: return "2" // + command
|
// case .selectVisibilityUnlisted: return "2" // + command
|
||||||
case .selectVisibilityPrivate: return "3" // + command
|
case .selectVisibilityPrivate: return "2" // + command
|
||||||
case .selectVisibilityDirect: return "4" // + command
|
case .selectVisibilityDirect: return "3" // + command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1338,7 +1340,7 @@ extension ComposeViewController {
|
||||||
case .togglePoll: return [.shift, .command]
|
case .togglePoll: return [.shift, .command]
|
||||||
case .toggleContentWarning: return [.shift, .command]
|
case .toggleContentWarning: return [.shift, .command]
|
||||||
case .selectVisibilityPublic: return [.command]
|
case .selectVisibilityPublic: return [.command]
|
||||||
case .selectVisibilityUnlisted: return [.command]
|
// case .selectVisibilityUnlisted: return [.command]
|
||||||
case .selectVisibilityPrivate: return [.command]
|
case .selectVisibilityPrivate: return [.command]
|
||||||
case .selectVisibilityDirect: return [.command]
|
case .selectVisibilityDirect: return [.command]
|
||||||
}
|
}
|
||||||
|
@ -1390,8 +1392,8 @@ extension ComposeViewController {
|
||||||
composeToolbarView.contentWarningButton.sendActions(for: .touchUpInside)
|
composeToolbarView.contentWarningButton.sendActions(for: .touchUpInside)
|
||||||
case .selectVisibilityPublic:
|
case .selectVisibilityPublic:
|
||||||
viewModel.selectedStatusVisibility.value = .public
|
viewModel.selectedStatusVisibility.value = .public
|
||||||
case .selectVisibilityUnlisted:
|
// case .selectVisibilityUnlisted:
|
||||||
viewModel.selectedStatusVisibility.value = .unlisted
|
// viewModel.selectedStatusVisibility.value = .unlisted
|
||||||
case .selectVisibilityPrivate:
|
case .selectVisibilityPrivate:
|
||||||
viewModel.selectedStatusVisibility.value = .private
|
viewModel.selectedStatusVisibility.value = .private
|
||||||
case .selectVisibilityDirect:
|
case .selectVisibilityDirect:
|
||||||
|
|
|
@ -92,7 +92,8 @@ final class ComposeViewModel {
|
||||||
self.activeAuthentication = CurrentValueSubject(context.authenticationService.activeMastodonAuthentication.value)
|
self.activeAuthentication = CurrentValueSubject(context.authenticationService.activeMastodonAuthentication.value)
|
||||||
self.activeAuthenticationBox = CurrentValueSubject(context.authenticationService.activeMastodonAuthenticationBox.value)
|
self.activeAuthenticationBox = CurrentValueSubject(context.authenticationService.activeMastodonAuthenticationBox.value)
|
||||||
// end init
|
// end init
|
||||||
if case let .reply(repliedToStatusObjectID) = composeKind {
|
switch composeKind {
|
||||||
|
case .reply(let repliedToStatusObjectID):
|
||||||
context.managedObjectContext.performAndWait {
|
context.managedObjectContext.performAndWait {
|
||||||
guard let status = context.managedObjectContext.object(with: repliedToStatusObjectID) as? Status else { return }
|
guard let status = context.managedObjectContext.object(with: repliedToStatusObjectID) as? Status else { return }
|
||||||
let composeAuthor: MastodonUser? = {
|
let composeAuthor: MastodonUser? = {
|
||||||
|
@ -124,14 +125,13 @@ final class ComposeViewModel {
|
||||||
self.preInsertedContent = preInsertedContent
|
self.preInsertedContent = preInsertedContent
|
||||||
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
||||||
}
|
}
|
||||||
|
case .hashtag(let hashtag):
|
||||||
} else if case let .hashtag(text) = composeKind {
|
let initialComposeContent = "#" + hashtag
|
||||||
let initialComposeContent = "#" + text
|
|
||||||
UITextChecker.learnWord(initialComposeContent)
|
UITextChecker.learnWord(initialComposeContent)
|
||||||
let preInsertedContent = initialComposeContent + " "
|
let preInsertedContent = initialComposeContent + " "
|
||||||
self.preInsertedContent = preInsertedContent
|
self.preInsertedContent = preInsertedContent
|
||||||
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
||||||
} else if case let .mention(mastodonUserObjectID) = composeKind {
|
case .mention(let mastodonUserObjectID):
|
||||||
context.managedObjectContext.performAndWait {
|
context.managedObjectContext.performAndWait {
|
||||||
let mastodonUser = context.managedObjectContext.object(with: mastodonUserObjectID) as! MastodonUser
|
let mastodonUser = context.managedObjectContext.object(with: mastodonUserObjectID) as! MastodonUser
|
||||||
let initialComposeContent = "@" + mastodonUser.acct
|
let initialComposeContent = "@" + mastodonUser.acct
|
||||||
|
@ -140,7 +140,7 @@ final class ComposeViewModel {
|
||||||
self.preInsertedContent = preInsertedContent
|
self.preInsertedContent = preInsertedContent
|
||||||
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
self.composeStatusAttribute.composeContent.value = preInsertedContent
|
||||||
}
|
}
|
||||||
} else {
|
case .post:
|
||||||
self.preInsertedContent = nil
|
self.preInsertedContent = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ final class ComposeViewModel {
|
||||||
}
|
}
|
||||||
// if preInsertedContent plus a space is equal to the content, simply dismiss the modal
|
// if preInsertedContent plus a space is equal to the content, simply dismiss the modal
|
||||||
if let preInsertedContent = self?.preInsertedContent {
|
if let preInsertedContent = self?.preInsertedContent {
|
||||||
return content == (preInsertedContent + " ")
|
return content == preInsertedContent
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -374,11 +374,6 @@ final class ComposeViewModel {
|
||||||
self.isPollToolbarButtonEnabled.value = !shouldPollDisable
|
self.isPollToolbarButtonEnabled.value = !shouldPollDisable
|
||||||
})
|
})
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
if let preInsertedContent = preInsertedContent {
|
|
||||||
// add a space after the injected text
|
|
||||||
composeStatusAttribute.composeContent.send(preInsertedContent + " ")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Combine
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
protocol ComposeToolbarViewDelegate: AnyObject {
|
protocol ComposeToolbarViewDelegate: AnyObject {
|
||||||
|
@ -19,6 +20,8 @@ protocol ComposeToolbarViewDelegate: AnyObject {
|
||||||
|
|
||||||
final class ComposeToolbarView: UIView {
|
final class ComposeToolbarView: UIView {
|
||||||
|
|
||||||
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
static let toolbarButtonSize: CGSize = CGSize(width: 44, height: 44)
|
static let toolbarButtonSize: CGSize = CGSize(width: 44, height: 44)
|
||||||
static let toolbarHeight: CGFloat = 44
|
static let toolbarHeight: CGFloat = 44
|
||||||
|
|
||||||
|
@ -76,6 +79,8 @@ final class ComposeToolbarView: UIView {
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let activeVisibilityType = CurrentValueSubject<VisibilitySelectionType, Never>(.public)
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
_init()
|
_init()
|
||||||
|
@ -142,6 +147,15 @@ extension ComposeToolbarView {
|
||||||
visibilityButton.showsMenuAsPrimaryAction = true
|
visibilityButton.showsMenuAsPrimaryAction = true
|
||||||
|
|
||||||
updateToolbarButtonUserInterfaceStyle()
|
updateToolbarButtonUserInterfaceStyle()
|
||||||
|
|
||||||
|
// update menu when selected visibility type changed
|
||||||
|
activeVisibilityType
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink { [weak self] type in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.visibilityButton.menu = self.createVisibilityContextMenu(interfaceStyle: self.traitCollection.userInterfaceStyle)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
@ -161,14 +175,15 @@ extension ComposeToolbarView {
|
||||||
|
|
||||||
enum VisibilitySelectionType: String, CaseIterable {
|
enum VisibilitySelectionType: String, CaseIterable {
|
||||||
case `public`
|
case `public`
|
||||||
case unlisted
|
// TODO: remove unlisted option from codebase
|
||||||
|
// case unlisted
|
||||||
case `private`
|
case `private`
|
||||||
case direct
|
case direct
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .public: return L10n.Scene.Compose.Visibility.public
|
case .public: return L10n.Scene.Compose.Visibility.public
|
||||||
case .unlisted: return L10n.Scene.Compose.Visibility.unlisted
|
// case .unlisted: return L10n.Scene.Compose.Visibility.unlisted
|
||||||
case .private: return L10n.Scene.Compose.Visibility.private
|
case .private: return L10n.Scene.Compose.Visibility.private
|
||||||
case .direct: return L10n.Scene.Compose.Visibility.direct
|
case .direct: return L10n.Scene.Compose.Visibility.direct
|
||||||
}
|
}
|
||||||
|
@ -181,7 +196,7 @@ extension ComposeToolbarView {
|
||||||
case .light: return UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
|
case .light: return UIImage(systemName: "person.3", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
|
||||||
default: return UIImage(systemName: "person.3.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
|
default: return UIImage(systemName: "person.3.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .medium))!
|
||||||
}
|
}
|
||||||
case .unlisted: return UIImage(systemName: "eye.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular))!
|
// case .unlisted: return UIImage(systemName: "eye.slash", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular))!
|
||||||
case .private: return UIImage(systemName: "person.crop.circle.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular))!
|
case .private: return UIImage(systemName: "person.crop.circle.badge.plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular))!
|
||||||
case .direct: return UIImage(systemName: "at", withConfiguration: UIImage.SymbolConfiguration(pointSize: 19, weight: .regular))!
|
case .direct: return UIImage(systemName: "at", withConfiguration: UIImage.SymbolConfiguration(pointSize: 19, weight: .regular))!
|
||||||
}
|
}
|
||||||
|
@ -190,7 +205,7 @@ extension ComposeToolbarView {
|
||||||
func imageNameForTimeline() -> String {
|
func imageNameForTimeline() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .public: return "person.3"
|
case .public: return "person.3"
|
||||||
case .unlisted: return "eye.slash"
|
// case .unlisted: return "eye.slash"
|
||||||
case .private: return "person.crop.circle.badge.plus"
|
case .private: return "person.crop.circle.badge.plus"
|
||||||
case .direct: return "at"
|
case .direct: return "at"
|
||||||
}
|
}
|
||||||
|
@ -199,7 +214,7 @@ extension ComposeToolbarView {
|
||||||
var visibility: Mastodon.Entity.Status.Visibility {
|
var visibility: Mastodon.Entity.Status.Visibility {
|
||||||
switch self {
|
switch self {
|
||||||
case .public: return .public
|
case .public: return .public
|
||||||
case .unlisted: return .unlisted
|
// case .unlisted: return .unlisted
|
||||||
case .private: return .private
|
case .private: return .private
|
||||||
case .direct: return .direct
|
case .direct: return .direct
|
||||||
}
|
}
|
||||||
|
@ -268,7 +283,8 @@ extension ComposeToolbarView {
|
||||||
|
|
||||||
private func createVisibilityContextMenu(interfaceStyle: UIUserInterfaceStyle) -> UIMenu {
|
private func createVisibilityContextMenu(interfaceStyle: UIUserInterfaceStyle) -> UIMenu {
|
||||||
let children: [UIMenuElement] = VisibilitySelectionType.allCases.map { type in
|
let children: [UIMenuElement] = VisibilitySelectionType.allCases.map { type in
|
||||||
UIAction(title: type.title, image: type.image(interfaceStyle: interfaceStyle), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] action in
|
let state: UIMenuElement.State = activeVisibilityType.value == type ? .on : .off
|
||||||
|
return UIAction(title: type.title, image: type.image(interfaceStyle: interfaceStyle), identifier: nil, discoverabilityTitle: nil, attributes: [], state: state) { [weak self] action in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: visibilitySelectionType: %s", ((#file as NSString).lastPathComponent), #line, #function, type.rawValue)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: visibilitySelectionType: %s", ((#file as NSString).lastPathComponent), #line, #function, type.rawValue)
|
||||||
self.delegate?.composeToolbarView(self, visibilityButtonDidPressed: self.visibilityButton, visibilitySelectionType: type)
|
self.delegate?.composeToolbarView(self, visibilityButtonDidPressed: self.visibilityButton, visibilitySelectionType: type)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import CoreData
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
extension NotificationViewModel {
|
extension NotificationViewModel {
|
||||||
func setupDiffableDataSource(
|
func setupDiffableDataSource(
|
||||||
|
@ -16,7 +17,7 @@ extension NotificationViewModel {
|
||||||
delegate: NotificationTableViewCellDelegate,
|
delegate: NotificationTableViewCellDelegate,
|
||||||
dependency: NeedsDependency
|
dependency: NeedsDependency
|
||||||
) {
|
) {
|
||||||
let timestampUpdatePublisher = Timer.publish(every: 30.0, on: .main, in: .common)
|
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
|
||||||
.autoconnect()
|
.autoconnect()
|
||||||
.share()
|
.share()
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -44,7 +45,14 @@ extension NotificationViewModel: NSFetchedResultsControllerDelegate {
|
||||||
|
|
||||||
guard let diffableDataSource = self.diffableDataSource else { return }
|
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||||
|
|
||||||
let predicate = fetchedResultsController.fetchRequest.predicate
|
let predicate: NSPredicate = {
|
||||||
|
let notificationTypePredicate = MastodonNotification.predicate(
|
||||||
|
validTypesRaws: Mastodon.Entity.Notification.NotificationType.knownCases.map { $0.rawValue }
|
||||||
|
)
|
||||||
|
return fetchedResultsController.fetchRequest.predicate.flatMap {
|
||||||
|
NSCompoundPredicate(andPredicateWithSubpredicates: [$0, notificationTypePredicate])
|
||||||
|
} ?? notificationTypePredicate
|
||||||
|
}()
|
||||||
let parentManagedObjectContext = fetchedResultsController.managedObjectContext
|
let parentManagedObjectContext = fetchedResultsController.managedObjectContext
|
||||||
let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
||||||
managedObjectContext.parent = parentManagedObjectContext
|
managedObjectContext.parent = parentManagedObjectContext
|
||||||
|
@ -73,19 +81,6 @@ extension NotificationViewModel: NSFetchedResultsControllerDelegate {
|
||||||
newSnapshot.appendSections([.main])
|
newSnapshot.appendSections([.main])
|
||||||
let items: [NotificationItem] = notifications.map { notification in
|
let items: [NotificationItem] = notifications.map { notification in
|
||||||
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
|
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
|
||||||
|
|
||||||
// let attribute: Item.StatusAttribute = {
|
|
||||||
// if let attribute = oldSnapshotAttributeDict[notification.objectID] {
|
|
||||||
// return attribute
|
|
||||||
// } else if let status = notification.status {
|
|
||||||
// let attribute = Item.StatusAttribute()
|
|
||||||
// let isSensitive = status.sensitive || !(status.spoilerText ?? "").isEmpty
|
|
||||||
// attribute.isRevealing.value = !isSensitive
|
|
||||||
// return attribute
|
|
||||||
// } else {
|
|
||||||
// return Item.StatusAttribute()
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
return NotificationItem.notification(objectID: notification.objectID, attribute: attribute)
|
return NotificationItem.notification(objectID: notification.objectID, attribute: attribute)
|
||||||
}
|
}
|
||||||
newSnapshot.appendItems(items, toSection: .main)
|
newSnapshot.appendItems(items, toSection: .main)
|
||||||
|
|
|
@ -17,7 +17,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
var pollCountdownSubscription: AnyCancellable?
|
var pollCountdownSubscription: AnyCancellable?
|
||||||
var delegate: NotificationTableViewCellDelegate?
|
var delegate: NotificationTableViewCellDelegate?
|
||||||
|
|
||||||
let avatatImageView: UIImageView = {
|
let avatarImageView: UIImageView = {
|
||||||
let imageView = UIImageView()
|
let imageView = UIImageView()
|
||||||
imageView.layer.cornerRadius = 4
|
imageView.layer.cornerRadius = 4
|
||||||
imageView.layer.cornerCurve = .continuous
|
imageView.layer.cornerCurve = .continuous
|
||||||
|
@ -86,7 +86,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
avatatImageView.af.cancelImageRequest()
|
avatarImageView.af.cancelImageRequest()
|
||||||
statusView.updateContentWarningDisplay(isHidden: true, animated: false)
|
statusView.updateContentWarningDisplay(isHidden: true, animated: false)
|
||||||
statusView.pollTableView.dataSource = nil
|
statusView.pollTableView.dataSource = nil
|
||||||
statusView.playerContainerView.reset()
|
statusView.playerContainerView.reset()
|
||||||
|
@ -142,13 +142,13 @@ extension NotificationStatusTableViewCell {
|
||||||
avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1)
|
avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1)
|
||||||
])
|
])
|
||||||
|
|
||||||
avatarContainer.addSubview(avatatImageView)
|
avatarContainer.addSubview(avatarImageView)
|
||||||
avatatImageView.translatesAutoresizingMaskIntoConstraints = false
|
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
avatatImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1),
|
avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1),
|
||||||
avatatImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1),
|
avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1),
|
||||||
avatatImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor),
|
avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor),
|
||||||
avatatImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor)
|
avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor)
|
||||||
])
|
])
|
||||||
|
|
||||||
avatarContainer.addSubview(actionImageBackground)
|
avatarContainer.addSubview(actionImageBackground)
|
||||||
|
|
|
@ -13,7 +13,7 @@ extension APIService {
|
||||||
|
|
||||||
func uploadMedia(
|
func uploadMedia(
|
||||||
domain: String,
|
domain: String,
|
||||||
query: Mastodon.API.Media.UploadMeidaQuery,
|
query: Mastodon.API.Media.UploadMediaQuery,
|
||||||
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
|
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error> {
|
||||||
let authorization = mastodonAuthenticationBox.userAuthorization
|
let authorization = mastodonAuthenticationBox.userAuthorization
|
||||||
|
|
|
@ -27,7 +27,7 @@ extension APIService {
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> {
|
||||||
let authorization = authorizationBox.userAuthorization
|
let authorization = authorizationBox.userAuthorization
|
||||||
let requestMastodonUserID = authorizationBox.userID
|
let requestMastodonUserID = authorizationBox.userID
|
||||||
let query = Mastodon.API.Account.AccountStatuseseQuery(
|
let query = Mastodon.API.Account.AccountStatusesQuery(
|
||||||
maxID: maxID,
|
maxID: maxID,
|
||||||
sinceID: sinceID,
|
sinceID: sinceID,
|
||||||
excludeReplies: excludeReplies,
|
excludeReplies: excludeReplies,
|
||||||
|
|
|
@ -56,7 +56,7 @@ extension MastodonAttachmentService.UploadState {
|
||||||
guard let file = service.file.value else { return }
|
guard let file = service.file.value else { return }
|
||||||
|
|
||||||
let description = service.description.value
|
let description = service.description.value
|
||||||
let query = Mastodon.API.Media.UploadMeidaQuery(
|
let query = Mastodon.API.Media.UploadMediaQuery(
|
||||||
file: file,
|
file: file,
|
||||||
thumbnail: nil,
|
thumbnail: nil,
|
||||||
description: description,
|
description: description,
|
||||||
|
|
|
@ -71,14 +71,14 @@ extension Mastodon.API.Account {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - session: `URLSession`
|
/// - session: `URLSession`
|
||||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
/// - query: `AccountStatuseseQuery` with query parameters
|
/// - query: `AccountStatusesQuery` with query parameters
|
||||||
/// - authorization: User token
|
/// - authorization: User token
|
||||||
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||||
public static func statuses(
|
public static func statuses(
|
||||||
session: URLSession,
|
session: URLSession,
|
||||||
domain: String,
|
domain: String,
|
||||||
accountID: Mastodon.Entity.Account.ID,
|
accountID: Mastodon.Entity.Account.ID,
|
||||||
query: AccountStatuseseQuery,
|
query: AccountStatusesQuery,
|
||||||
authorization: Mastodon.API.OAuth.Authorization
|
authorization: Mastodon.API.OAuth.Authorization
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> {
|
||||||
let request = Mastodon.API.get(
|
let request = Mastodon.API.get(
|
||||||
|
@ -94,7 +94,7 @@ extension Mastodon.API.Account {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AccountStatuseseQuery: GetQuery {
|
public struct AccountStatusesQuery: GetQuery {
|
||||||
public let maxID: Mastodon.Entity.Status.ID?
|
public let maxID: Mastodon.Entity.Status.ID?
|
||||||
public let sinceID: Mastodon.Entity.Status.ID?
|
public let sinceID: Mastodon.Entity.Status.ID?
|
||||||
public let excludeReplies: Bool? // undocumented
|
public let excludeReplies: Bool? // undocumented
|
||||||
|
|
|
@ -33,7 +33,7 @@ extension Mastodon.API.Media {
|
||||||
public static func uploadMedia(
|
public static func uploadMedia(
|
||||||
session: URLSession,
|
session: URLSession,
|
||||||
domain: String,
|
domain: String,
|
||||||
query: UploadMeidaQuery,
|
query: UploadMediaQuery,
|
||||||
authorization: Mastodon.API.OAuth.Authorization?
|
authorization: Mastodon.API.OAuth.Authorization?
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Attachment>, Error> {
|
||||||
var request = Mastodon.API.post(
|
var request = Mastodon.API.post(
|
||||||
|
@ -56,7 +56,7 @@ extension Mastodon.API.Media {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UploadMeidaQuery: PostQuery, PutQuery {
|
public struct UploadMediaQuery: PostQuery, PutQuery {
|
||||||
public let file: Mastodon.Query.MediaAttachment?
|
public let file: Mastodon.Query.MediaAttachment?
|
||||||
public let thumbnail: Mastodon.Query.MediaAttachment?
|
public let thumbnail: Mastodon.Query.MediaAttachment?
|
||||||
public let description: String?
|
public let description: String?
|
||||||
|
|
|
@ -49,6 +49,18 @@ extension Mastodon.Entity.Notification {
|
||||||
|
|
||||||
case _other(String)
|
case _other(String)
|
||||||
|
|
||||||
|
public static var knownCases: [NotificationType] {
|
||||||
|
return [
|
||||||
|
.follow,
|
||||||
|
.followRequest,
|
||||||
|
.mention,
|
||||||
|
.reblog,
|
||||||
|
.favourite,
|
||||||
|
.poll,
|
||||||
|
.status
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
public init?(rawValue: String) {
|
public init?(rawValue: String) {
|
||||||
switch rawValue {
|
switch rawValue {
|
||||||
case "follow": self = .follow
|
case "follow": self = .follow
|
||||||
|
|
Loading…
Reference in New Issue