feat: add interaction for follow request notification
This commit is contained in:
parent
44b06f3a6b
commit
bcfdaf2ca7
|
@ -114,7 +114,7 @@
|
|||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>34</integer>
|
||||
<integer>24</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -129,12 +129,12 @@
|
|||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>32</integer>
|
||||
<integer>23</integer>
|
||||
</dict>
|
||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>33</integer>
|
||||
<integer>22</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -14,7 +14,7 @@ extension DataSourceFacade {
|
|||
user: ManagedObjectRecord<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
_ = try await dependency.context.apiService.toggleBlock(
|
||||
|
|
|
@ -15,7 +15,7 @@ extension DataSourceFacade {
|
|||
status: ManagedObjectRecord<Status>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
_ = try await provider.context.apiService.favorite(
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import UIKit
|
||||
import CoreDataStack
|
||||
import class CoreDataStack.Notification
|
||||
import MastodonSDK
|
||||
|
||||
extension DataSourceFacade {
|
||||
static func responseToUserFollowAction(
|
||||
|
@ -14,7 +16,7 @@ extension DataSourceFacade {
|
|||
user: ManagedObjectRecord<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
_ = try await dependency.context.apiService.toggleFollow(
|
||||
|
@ -23,3 +25,32 @@ extension DataSourceFacade {
|
|||
)
|
||||
} // end func
|
||||
}
|
||||
|
||||
extension DataSourceFacade {
|
||||
static func responseToUserFollowRequestAction(
|
||||
dependency: NeedsDependency,
|
||||
notification: ManagedObjectRecord<Notification>,
|
||||
query: Mastodon.API.Account.FollowReqeustQuery,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
let managedObjectContext = dependency.context.managedObjectContext
|
||||
let _userID: MastodonUser.ID? = try await managedObjectContext.perform {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return nil }
|
||||
return notification.account.id
|
||||
}
|
||||
|
||||
guard let userID = _userID else {
|
||||
assertionFailure()
|
||||
throw APIService.APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
_ = try await dependency.context.apiService.followRequest(
|
||||
userID: userID,
|
||||
query: query,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
} // end func
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ extension DataSourceFacade {
|
|||
user: ManagedObjectRecord<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
_ = try await dependency.context.apiService.toggleMute(
|
||||
|
|
|
@ -15,7 +15,7 @@ extension DataSourceFacade {
|
|||
status: ManagedObjectRecord<Status>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = UISelectionFeedbackGenerator()
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
_ = try await provider.context.apiService.reblog(
|
||||
|
|
|
@ -87,6 +87,68 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider {
|
|||
}
|
||||
}
|
||||
|
||||
extension NotificationTableViewCellDelegate where Self: DataSourceProvider {
|
||||
|
||||
func tableViewCell(
|
||||
_ cell: UITableViewCell,
|
||||
notificationView: NotificationView,
|
||||
acceptFollowRequestButtonDidPressed button: UIButton
|
||||
) {
|
||||
Task {
|
||||
let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
|
||||
guard let item = await item(from: source) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
guard case let .notification(notification) = item else {
|
||||
assertionFailure("only works for status data provider")
|
||||
return
|
||||
}
|
||||
|
||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
return
|
||||
}
|
||||
|
||||
try await DataSourceFacade.responseToUserFollowRequestAction(
|
||||
dependency: self,
|
||||
notification: notification,
|
||||
query: .accept,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
} // end Task
|
||||
}
|
||||
|
||||
func tableViewCell(
|
||||
_ cell: UITableViewCell,
|
||||
notificationView: NotificationView,
|
||||
rejectFollowRequestButtonDidPressed button: UIButton
|
||||
) {
|
||||
Task {
|
||||
let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
|
||||
guard let item = await item(from: source) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
guard case let .notification(notification) = item else {
|
||||
assertionFailure("only works for status data provider")
|
||||
return
|
||||
}
|
||||
|
||||
guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
return
|
||||
}
|
||||
|
||||
try await DataSourceFacade.responseToUserFollowRequestAction(
|
||||
dependency: self,
|
||||
notification: notification,
|
||||
query: .reject,
|
||||
authenticationBox: authenticationBox
|
||||
)
|
||||
} // end Task
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Status Content
|
||||
extension NotificationTableViewCellDelegate where Self: DataSourceProvider {
|
||||
func tableViewCell(
|
||||
|
|
|
@ -25,6 +25,8 @@ protocol NotificationTableViewCellDelegate: AnyObject, AutoGenerateProtocolDeleg
|
|||
// sourcery:inline:NotificationTableViewCellDelegate.AutoGenerateProtocolDelegate
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, authorAvatarButtonDidPressed button: AvatarButton)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, acceptFollowRequestButtonDidPressed button: UIButton)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, rejectFollowRequestButtonDidPressed button: UIButton)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView)
|
||||
func tableViewCell(_ cell: UITableViewCell, notificationView: NotificationView, statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaView: MediaView, didSelectMediaViewAt index: Int)
|
||||
|
@ -49,6 +51,14 @@ extension NotificationViewDelegate where Self: NotificationViewContainerTableVie
|
|||
delegate?.tableViewCell(self, notificationView: notificationView, menuButton: button, didSelectAction: action)
|
||||
}
|
||||
|
||||
func notificationView(_ notificationView: NotificationView, acceptFollowRequestButtonDidPressed button: UIButton) {
|
||||
delegate?.tableViewCell(self, notificationView: notificationView, acceptFollowRequestButtonDidPressed: button)
|
||||
}
|
||||
|
||||
func notificationView(_ notificationView: NotificationView, rejectFollowRequestButtonDidPressed button: UIButton) {
|
||||
delegate?.tableViewCell(self, notificationView: notificationView, rejectFollowRequestButtonDidPressed: button)
|
||||
}
|
||||
|
||||
func notificationView(_ notificationView: NotificationView, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta) {
|
||||
delegate?.tableViewCell(self, notificationView: notificationView, statusView: statusView, metaText: metaText, didSelectMeta: meta)
|
||||
}
|
||||
|
|
|
@ -36,23 +36,26 @@ extension NotificationView {
|
|||
return
|
||||
}
|
||||
|
||||
if let status = notification.status {
|
||||
switch type {
|
||||
case .follow, .followRequest:
|
||||
case .follow:
|
||||
setAuthorContainerBottomPaddingViewDisplay()
|
||||
case .followRequest:
|
||||
setFollowRequestAdaptiveMarginContainerViewDisplay()
|
||||
case .mention, .status:
|
||||
if let status = notification.status {
|
||||
statusView.configure(status: status)
|
||||
setStatusViewDisplay()
|
||||
}
|
||||
case .reblog, .favourite, .poll:
|
||||
if let status = notification.status {
|
||||
quoteStatusView.configure(status: status)
|
||||
setQuoteStatusViewDisplay()
|
||||
}
|
||||
case ._other:
|
||||
setAuthorContainerBottomPaddingViewDisplay()
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
setAuthorContainerBottomPaddingViewDisplay()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,91 +15,42 @@ import CommonOSLog
|
|||
import MastodonSDK
|
||||
|
||||
extension APIService {
|
||||
// func acceptFollowRequest(
|
||||
// mastodonUserID: MastodonUser.ID,
|
||||
// mastodonAuthenticationBox: MastodonAuthenticationBox
|
||||
// ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
|
||||
// let domain = mastodonAuthenticationBox.domain
|
||||
// let authorization = mastodonAuthenticationBox.userAuthorization
|
||||
// let requestMastodonUserID = mastodonAuthenticationBox.userID
|
||||
//
|
||||
// return Mastodon.API.Account.acceptFollowRequest(
|
||||
// session: session,
|
||||
// domain: domain,
|
||||
// userID: mastodonUserID,
|
||||
// authorization: authorization)
|
||||
// .flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> in
|
||||
// let managedObjectContext = self.backgroundManagedObjectContext
|
||||
// return managedObjectContext.performChanges {
|
||||
// let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||
// requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: requestMastodonUserID)
|
||||
// requestMastodonUserRequest.fetchLimit = 1
|
||||
// guard let requestMastodonUser = managedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
|
||||
//
|
||||
// let lookUpMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||
// lookUpMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: mastodonUserID)
|
||||
// lookUpMastodonUserRequest.fetchLimit = 1
|
||||
// let lookUpMastodonuser = managedObjectContext.safeFetch(lookUpMastodonUserRequest).first
|
||||
//
|
||||
// if let lookUpMastodonuser = lookUpMastodonuser {
|
||||
// let entity = response.value
|
||||
// APIService.CoreData.update(user: lookUpMastodonuser, entity: entity, requestMastodonUser: requestMastodonUser, domain: domain, networkDate: response.networkDate)
|
||||
// }
|
||||
// }
|
||||
// .tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Relationship> in
|
||||
// switch result {
|
||||
// case .success:
|
||||
// return response
|
||||
// case .failure(let error):
|
||||
// throw error
|
||||
// }
|
||||
// }
|
||||
// .eraseToAnyPublisher()
|
||||
// }
|
||||
// .eraseToAnyPublisher()
|
||||
// }
|
||||
|
||||
// func rejectFollowRequest(
|
||||
// mastodonUserID: MastodonUser.ID,
|
||||
// mastodonAuthenticationBox: MastodonAuthenticationBox
|
||||
// ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
|
||||
// let domain = mastodonAuthenticationBox.domain
|
||||
// let authorization = mastodonAuthenticationBox.userAuthorization
|
||||
// let requestMastodonUserID = mastodonAuthenticationBox.userID
|
||||
//
|
||||
// return Mastodon.API.Account.rejectFollowRequest(
|
||||
// session: session,
|
||||
// domain: domain,
|
||||
// userID: mastodonUserID,
|
||||
// authorization: authorization)
|
||||
// .flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> in
|
||||
// let managedObjectContext = self.backgroundManagedObjectContext
|
||||
// return managedObjectContext.performChanges {
|
||||
// let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||
// requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: requestMastodonUserID)
|
||||
// requestMastodonUserRequest.fetchLimit = 1
|
||||
// guard let requestMastodonUser = managedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
|
||||
//
|
||||
// let lookUpMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||
// lookUpMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: mastodonUserID)
|
||||
// lookUpMastodonUserRequest.fetchLimit = 1
|
||||
// let lookUpMastodonuser = managedObjectContext.safeFetch(lookUpMastodonUserRequest).first
|
||||
//
|
||||
// if let lookUpMastodonuser = lookUpMastodonuser {
|
||||
// let entity = response.value
|
||||
// APIService.CoreData.update(user: lookUpMastodonuser, entity: entity, requestMastodonUser: requestMastodonUser, domain: domain, networkDate: response.networkDate)
|
||||
// }
|
||||
// }
|
||||
// .tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Relationship> in
|
||||
// switch result {
|
||||
// case .success:
|
||||
// return response
|
||||
// case .failure(let error):
|
||||
// throw error
|
||||
// }
|
||||
// }
|
||||
// .eraseToAnyPublisher()
|
||||
// }
|
||||
// .eraseToAnyPublisher()
|
||||
// }
|
||||
func followRequest(
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
query: Mastodon.API.Account.FollowReqeustQuery,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
let response = try await Mastodon.API.Account.followRequest(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
userID: userID,
|
||||
query: query,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let request = MastodonUser.sortedFetchRequest
|
||||
request.predicate = MastodonUser.predicate(
|
||||
domain: authenticationBox.domain,
|
||||
id: authenticationBox.userID
|
||||
)
|
||||
request.fetchLimit = 1
|
||||
guard let user = managedObjectContext.safeFetch(request).first else { return }
|
||||
guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user else { return }
|
||||
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public enum MastodonNotificationType: RawRepresentable {
|
|||
public init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "follow": self = .follow
|
||||
case "followRequest": self = .followRequest
|
||||
case "follow_request": self = .followRequest
|
||||
case "mention": self = .mention
|
||||
case "reblog": self = .reblog
|
||||
case "favourite": self = .favourite
|
||||
|
@ -34,7 +34,7 @@ public enum MastodonNotificationType: RawRepresentable {
|
|||
public var rawValue: String {
|
||||
switch self {
|
||||
case .follow: return "follow"
|
||||
case .followRequest: return "followRequest"
|
||||
case .followRequest: return "follow_request"
|
||||
case .mention: return "mention"
|
||||
case .reblog: return "reblog"
|
||||
case .favourite: return "favourite"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/checkmark.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/checkmark.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "checkmark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
76
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/checkmark.imageset/checkmark.pdf
vendored
Normal file
76
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/checkmark.imageset/checkmark.pdf
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 3.250000 5.105469 cm
|
||||
0.129412 0.129412 0.129412 scn
|
||||
1.280330 5.924861 m
|
||||
0.987437 6.217754 0.512563 6.217754 0.219670 5.924861 c
|
||||
-0.073223 5.631968 -0.073223 5.157095 0.219670 4.864202 c
|
||||
4.719670 0.364201 l
|
||||
5.012563 0.071307 5.487436 0.071307 5.780330 0.364201 c
|
||||
16.780331 11.364201 l
|
||||
17.073223 11.657094 17.073223 12.131968 16.780331 12.424861 c
|
||||
16.487438 12.717754 16.012562 12.717754 15.719669 12.424861 c
|
||||
5.250000 1.955191 l
|
||||
1.280330 5.924861 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
523
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000613 00000 n
|
||||
0000000635 00000 n
|
||||
0000000808 00000 n
|
||||
0000000882 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
941
|
||||
%%EOF
|
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/xmark.imageset/Contents.json
vendored
Normal file
15
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/xmark.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "xmark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
89
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/xmark.imageset/xmark.pdf
vendored
Normal file
89
MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Editing/xmark.imageset/xmark.pdf
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.250000 4.105469 cm
|
||||
0.129412 0.129412 0.129412 scn
|
||||
0.147052 15.340743 m
|
||||
0.219670 15.424861 l
|
||||
0.485936 15.691128 0.902600 15.715334 1.196212 15.497479 c
|
||||
1.280330 15.424861 l
|
||||
7.750000 8.955531 l
|
||||
14.219669 15.424861 l
|
||||
14.512563 15.717754 14.987437 15.717754 15.280331 15.424861 c
|
||||
15.573224 15.131968 15.573224 14.657094 15.280331 14.364201 c
|
||||
8.811000 7.894531 l
|
||||
15.280331 1.424862 l
|
||||
15.546597 1.158595 15.570804 0.741932 15.352949 0.448320 c
|
||||
15.280331 0.364201 l
|
||||
15.014064 0.097934 14.597401 0.073728 14.303789 0.291582 c
|
||||
14.219669 0.364201 l
|
||||
7.750000 6.833531 l
|
||||
1.280330 0.364201 l
|
||||
0.987437 0.071307 0.512563 0.071307 0.219670 0.364201 c
|
||||
-0.073223 0.657094 -0.073223 1.131968 0.219670 1.424862 c
|
||||
6.689000 7.894531 l
|
||||
0.219670 14.364201 l
|
||||
-0.046597 14.630467 -0.070803 15.047132 0.147052 15.340743 c
|
||||
0.219670 15.424861 l
|
||||
0.147052 15.340743 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
914
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001004 00000 n
|
||||
0000001026 00000 n
|
||||
0000001199 00000 n
|
||||
0000001273 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1332
|
||||
%%EOF
|
|
@ -94,6 +94,10 @@ public enum Asset {
|
|||
public enum Connectivity {
|
||||
public static let photoFillSplit = ImageAsset(name: "Connectivity/photo.fill.split")
|
||||
}
|
||||
public enum Editing {
|
||||
public static let checkmark = ImageAsset(name: "Editing/checkmark")
|
||||
public static let xmark = ImageAsset(name: "Editing/xmark")
|
||||
}
|
||||
public enum Human {
|
||||
public static let eyeCircleFill = ImageAsset(name: "Human/eye.circle.fill")
|
||||
public static let eyeSlashCircleFill = ImageAsset(name: "Human/eye.slash.circle.fill")
|
||||
|
|
|
@ -89,3 +89,37 @@ extension Mastodon.API.Account {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
public enum FollowReqeustQuery {
|
||||
case accept
|
||||
case reject
|
||||
}
|
||||
|
||||
public static func followRequest(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
query: FollowReqeustQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
|
||||
switch query {
|
||||
case .accept:
|
||||
return acceptFollowRequest(
|
||||
session: session,
|
||||
domain: domain,
|
||||
userID: userID,
|
||||
authorization: authorization
|
||||
)
|
||||
case .reject:
|
||||
return rejectFollowRequest(
|
||||
session: session,
|
||||
domain: domain,
|
||||
userID: userID,
|
||||
authorization: authorization
|
||||
)
|
||||
} // end switch
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ public protocol NotificationViewDelegate: AnyObject {
|
|||
func notificationView(_ notificationView: NotificationView, authorAvatarButtonDidPressed button: AvatarButton)
|
||||
func notificationView(_ notificationView: NotificationView, menuButton button: UIButton, didSelectAction action: MastodonMenu.Action)
|
||||
|
||||
func notificationView(_ notificationView: NotificationView, acceptFollowRequestButtonDidPressed button: UIButton)
|
||||
func notificationView(_ notificationView: NotificationView, rejectFollowRequestButtonDidPressed button: UIButton)
|
||||
|
||||
func notificationView(_ notificationView: NotificationView, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta)
|
||||
func notificationView(_ notificationView: NotificationView, statusView: StatusView, spoilerOverlayViewDidPressed overlayView: SpoilerOverlayView)
|
||||
func notificationView(_ notificationView: NotificationView, statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaView: MediaView, didSelectMediaViewAt index: Int)
|
||||
|
@ -101,6 +104,44 @@ public final class NotificationView: UIView {
|
|||
// notification type indicator imageView
|
||||
public let notificationTypeIndicatorLabel = MetaLabel(style: .notificationTitle)
|
||||
|
||||
// follow request
|
||||
let followRequestAdaptiveMarginContainerView = AdaptiveMarginContainerView()
|
||||
let followRequestContainerView = UIView()
|
||||
|
||||
let acceptFollowRequestButtonShadowBackgroundContainer = ShadowBackgroundContainer()
|
||||
private(set) lazy var acceptFollowRequestButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(Asset.Editing.checkmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||
button.imageView?.contentMode = .scaleAspectFit
|
||||
button.setBackgroundImage(.placeholder(color: .systemGreen), for: .normal)
|
||||
button.tintColor = .white
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.cornerCurve = .continuous
|
||||
button.layer.cornerRadius = 4
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.cornerRadius = 4
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.shadowAlpha = 0.1
|
||||
button.addTarget(self, action: #selector(NotificationView.acceptFollowRequestButtonDidPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
let rejectFollowRequestButtonShadowBackgroundContainer = ShadowBackgroundContainer()
|
||||
private(set) lazy var rejectFollowRequestButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(Asset.Editing.xmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||||
button.imageView?.contentMode = .scaleAspectFit
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2) // tweak xmark size
|
||||
button.setBackgroundImage(.placeholder(color: .systemRed), for: .normal)
|
||||
button.tintColor = .white
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.cornerCurve = .continuous
|
||||
button.layer.cornerRadius = 4
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.cornerRadius = 4
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.shadowAlpha = 0.1
|
||||
button.addTarget(self, action: #selector(NotificationView.rejectFollowRequestButtonDidPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
// status
|
||||
public let statusView = StatusView()
|
||||
|
||||
public let quoteStatusViewContainerView = UIView()
|
||||
|
@ -115,6 +156,8 @@ public final class NotificationView: UIView {
|
|||
|
||||
authorContainerViewBottomPaddingView.isHidden = true
|
||||
|
||||
followRequestAdaptiveMarginContainerView.isHidden = true
|
||||
|
||||
statusView.isHidden = true
|
||||
statusView.prepareForReuse()
|
||||
|
||||
|
@ -222,6 +265,46 @@ extension NotificationView {
|
|||
])
|
||||
authorContainerViewBottomPaddingView.isHidden = true
|
||||
|
||||
// follow reqeust
|
||||
followRequestAdaptiveMarginContainerView.contentView = followRequestContainerView
|
||||
followRequestAdaptiveMarginContainerView.margin = StatusView.containerLayoutMargin
|
||||
containerStackView.addArrangedSubview(followRequestAdaptiveMarginContainerView)
|
||||
|
||||
acceptFollowRequestButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.addSubview(acceptFollowRequestButton)
|
||||
NSLayoutConstraint.activate([
|
||||
acceptFollowRequestButton.topAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.topAnchor),
|
||||
acceptFollowRequestButton.leadingAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.leadingAnchor),
|
||||
acceptFollowRequestButton.trailingAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.trailingAnchor),
|
||||
acceptFollowRequestButton.bottomAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.bottomAnchor),
|
||||
])
|
||||
|
||||
rejectFollowRequestButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.addSubview(rejectFollowRequestButton)
|
||||
NSLayoutConstraint.activate([
|
||||
rejectFollowRequestButton.topAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.topAnchor),
|
||||
rejectFollowRequestButton.leadingAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.leadingAnchor),
|
||||
rejectFollowRequestButton.trailingAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.trailingAnchor),
|
||||
rejectFollowRequestButton.bottomAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.bottomAnchor),
|
||||
])
|
||||
|
||||
let followReqeustContainerBottomMargin: CGFloat = 8
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
followRequestContainerView.addSubview(acceptFollowRequestButtonShadowBackgroundContainer)
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
followRequestContainerView.addSubview(rejectFollowRequestButtonShadowBackgroundContainer)
|
||||
NSLayoutConstraint.activate([
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.topAnchor.constraint(equalTo: followRequestContainerView.topAnchor),
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.leadingAnchor.constraint(equalTo: followRequestContainerView.leadingAnchor),
|
||||
followRequestContainerView.bottomAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.bottomAnchor, constant: followReqeustContainerBottomMargin),
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.topAnchor.constraint(equalTo: followRequestContainerView.topAnchor),
|
||||
rejectFollowRequestButtonShadowBackgroundContainer.leadingAnchor.constraint(equalTo: acceptFollowRequestButtonShadowBackgroundContainer.trailingAnchor, constant: 8),
|
||||
followRequestContainerView.trailingAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.trailingAnchor),
|
||||
followRequestContainerView.bottomAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.bottomAnchor, constant: followReqeustContainerBottomMargin),
|
||||
acceptFollowRequestButtonShadowBackgroundContainer.widthAnchor.constraint(equalTo: rejectFollowRequestButtonShadowBackgroundContainer.widthAnchor),
|
||||
])
|
||||
followRequestAdaptiveMarginContainerView.isHidden = true
|
||||
|
||||
// statusView
|
||||
containerStackView.addArrangedSubview(statusView)
|
||||
statusView.setup(style: .notification)
|
||||
|
@ -271,10 +354,22 @@ extension NotificationView {
|
|||
}
|
||||
|
||||
extension NotificationView {
|
||||
|
||||
@objc private func avatarButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
delegate?.notificationView(self, authorAvatarButtonDidPressed: avatarButton)
|
||||
}
|
||||
|
||||
@objc private func acceptFollowRequestButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
delegate?.notificationView(self, acceptFollowRequestButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
@objc private func rejectFollowRequestButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
delegate?.notificationView(self, rejectFollowRequestButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NotificationView {
|
||||
|
@ -283,6 +378,10 @@ extension NotificationView {
|
|||
authorContainerViewBottomPaddingView.isHidden = false
|
||||
}
|
||||
|
||||
public func setFollowRequestAdaptiveMarginContainerViewDisplay() {
|
||||
followRequestAdaptiveMarginContainerView.isHidden = false
|
||||
}
|
||||
|
||||
public func setStatusViewDisplay() {
|
||||
statusView.isHidden = false
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue