Merge pull request #465 from mastodon/feature-follow-request-UI

Update follow request UI
This commit is contained in:
CMK 2022-07-15 04:00:11 +08:00 committed by GitHub
commit 89e7996a9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 333 additions and 34 deletions

View File

@ -361,9 +361,9 @@
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08327312970006A36CF /* APIService+Following.swift */; };
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08527312E67006A36CF /* WizardViewController.swift */; };
DB67D089273256D7006A36CF /* StoreReviewPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D088273256D7006A36CF /* StoreReviewPreference.swift */; };
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
DB68045B2636DC6A00430867 /* MastodonPushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonPushNotification.swift */; };
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
DB68046C2636DC9E00430867 /* MastodonPushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonPushNotification.swift */; };
DB6804832637CD4C00430867 /* AppShared.h in Headers */ = {isa = PBXBuildFile; fileRef = DB6804812637CD4C00430867 /* AppShared.h */; settings = {ATTRIBUTES = (Public, ); }; };
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; };
DB6804872637CD4C00430867 /* AppShared.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB68047F2637CD4C00430867 /* AppShared.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -1119,7 +1119,7 @@
DB67D08327312970006A36CF /* APIService+Following.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Following.swift"; sourceTree = "<group>"; };
DB67D08527312E67006A36CF /* WizardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardViewController.swift; sourceTree = "<group>"; };
DB67D088273256D7006A36CF /* StoreReviewPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreReviewPreference.swift; sourceTree = "<group>"; };
DB68045A2636DC6A00430867 /* MastodonNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonNotification.swift; sourceTree = "<group>"; };
DB68045A2636DC6A00430867 /* MastodonPushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonPushNotification.swift; sourceTree = "<group>"; };
DB68047F2637CD4C00430867 /* AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DB6804812637CD4C00430867 /* AppShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppShared.h; sourceTree = "<group>"; };
DB6804822637CD4C00430867 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -3347,7 +3347,7 @@
DB68053E2638011000430867 /* NotificationService.entitlements */,
DBF8AE15263293E400C9C23C /* NotificationService.swift */,
DB6D9F3426351B7A008423CD /* NotificationService+Decrypt.swift */,
DB68045A2636DC6A00430867 /* MastodonNotification.swift */,
DB68045A2636DC6A00430867 /* MastodonPushNotification.swift */,
DBCBCBF3267CB070000F5B51 /* Decode85.swift */,
DBF8AE17263293E400C9C23C /* Info.plist */,
);
@ -4332,7 +4332,7 @@
DBD5B1FA27BD013700BD6B38 /* DataSourceProvider+StatusTableViewControllerNavigateable.swift in Sources */,
DB5B549A2833A60400DEF8B2 /* FamiliarFollowersViewController.swift in Sources */,
DB3E6FE22806A50100B035AE /* DiscoveryHashtagsViewModel+Diffable.swift in Sources */,
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */,
DB68046C2636DC9E00430867 /* MastodonPushNotification.swift in Sources */,
DBAE3F9E2616E308004B8251 /* APIService+Mute.swift in Sources */,
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
DB6D9F57263577D2008423CD /* APIService+CoreData+Setting.swift in Sources */,
@ -4540,7 +4540,7 @@
buildActionMask = 2147483647;
files = (
DBE54ACC2636C8FD004E7C0B /* NotificationPreference.swift in Sources */,
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */,
DB68045B2636DC6A00430867 /* MastodonPushNotification.swift in Sources */,
DB6D9F3526351B7A008423CD /* NotificationService+Decrypt.swift in Sources */,
DB6804662636DC9000430867 /* String.swift in Sources */,
DBCBCBF4267CB070000F5B51 /* Decode85.swift in Sources */,
@ -5209,6 +5209,7 @@
DB848E2F282B5E6300A302CC /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 144;
@ -5534,6 +5535,7 @@
DBEB19E627E4658E00B0E80E /* Release Snapshot */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 144;
@ -5605,6 +5607,7 @@
DBF8AE1C263293E400C9C23C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 144;
@ -5628,6 +5631,7 @@
DBF8AE1D263293E400C9C23C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 144;

View File

@ -9,7 +9,7 @@
<key>isShown</key>
<true/>
<key>orderHint</key>
<integer>9</integer>
<integer>6</integer>
</dict>
<key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict>
@ -19,27 +19,27 @@
<key>Mastodon - Profile.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<integer>2</integer>
</dict>
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>12</integer>
<integer>7</integer>
</dict>
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<integer>3</integer>
</dict>
<key>Mastodon - Snapshot.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>7</integer>
<integer>4</integer>
</dict>
<key>Mastodon - ar.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>8</integer>
<integer>5</integer>
</dict>
<key>Mastodon - ar.xcscheme_^#shared#^_</key>
<dict>
@ -114,7 +114,7 @@
<key>MastodonIntent.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>29</integer>
<integer>22</integer>
</dict>
<key>MastodonIntents.xcscheme_^#shared#^_</key>
<dict>
@ -129,12 +129,12 @@
<key>NotificationService.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>31</integer>
<integer>23</integer>
</dict>
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>30</integer>
<integer>24</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -74,6 +74,7 @@ extension DataSourceFacade {
authenticationBox: authenticationBox
)
} catch {
// reset state when failure
try? await managedObjectContext.performChanges {
guard let notification = notification.object(in: managedObjectContext) else { return }
notification.transientFollowRequestState = .init(state: .none)
@ -111,7 +112,8 @@ extension DataSourceFacade {
case .accept:
notification.transientFollowRequestState = .init(state: .isAccept)
case .reject:
notification.transientFollowRequestState = .init(state: .isReject)
// do nothing due to will delete notification
break
}
}
@ -122,7 +124,11 @@ extension DataSourceFacade {
case .accept:
notification.followRequestState = .init(state: .isAccept)
case .reject:
notification.followRequestState = .init(state: .isReject)
// delete notification
for feed in notification.feeds {
backgroundManagedObjectContext.delete(feed)
}
backgroundManagedObjectContext.delete(notification)
}
}
} // end func

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "forbidden.20.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -0,0 +1,83 @@
%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 2.000000 2.000000 cm
0.000000 0.000000 0.000000 scn
16.000000 8.000000 m
16.000000 3.581722 12.418278 0.000000 8.000000 0.000000 c
3.581722 0.000000 0.000000 3.581722 0.000000 8.000000 c
0.000000 12.418278 3.581722 16.000000 8.000000 16.000000 c
12.418278 16.000000 16.000000 12.418278 16.000000 8.000000 c
h
14.500000 8.000000 m
14.500000 9.524690 13.975041 10.926769 13.096009 12.035351 c
3.964649 2.903991 l
5.073231 2.024959 6.475310 1.500000 8.000000 1.500000 c
11.589850 1.500000 14.500000 4.410150 14.500000 8.000000 c
h
2.903990 3.964651 m
12.035349 13.096010 l
10.926767 13.975041 9.524689 14.500000 8.000000 14.500000 c
4.410149 14.500000 1.500000 11.589851 1.500000 8.000000 c
1.500000 6.475311 2.024959 5.073233 2.903990 3.964651 c
h
f
n
Q
endstream
endobj
3 0 obj
819
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 20.000000 20.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
0000000909 00000 n
0000000931 00000 n
0000001104 00000 n
0000001178 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1237
%%EOF

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "checkmark.20.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View 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 2.250000 4.091309 cm
0.000000 0.000000 0.000000 scn
4.782121 2.001464 m
1.310565 5.906964 l
1.035376 6.216551 0.561322 6.244437 0.251735 5.969249 c
-0.057852 5.694060 -0.085737 5.220006 0.189451 4.910419 c
4.189451 0.410419 l
4.476144 0.087890 4.975201 0.073224 5.280338 0.378362 c
15.780338 10.878361 l
16.073231 11.171254 16.073231 11.646129 15.780338 11.939021 c
15.487445 12.231915 15.012570 12.231915 14.719677 11.939021 c
4.782121 2.001464 l
h
f
n
Q
endstream
endobj
3 0 obj
523
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 20.000000 20.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

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFC",
"green" : "0x2C",
"red" : "0x56"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.988",
"green" : "0.173",
"red" : "0.337"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.969",
"green" : "0.949",
"red" : "0.949"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.969",
"green" : "0.949",
"red" : "0.949"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -32,6 +32,7 @@ public enum Asset {
public static let mastodonTextLogo = ImageAsset(name: "Asset/mastodon.text.logo")
}
public enum Circles {
public static let forbidden20 = ImageAsset(name: "Circles/forbidden.20")
public static let plusCircleFill = ImageAsset(name: "Circles/plus.circle.fill")
public static let plusCircle = ImageAsset(name: "Circles/plus.circle")
}
@ -102,6 +103,7 @@ public enum Asset {
public static let photoFillSplit = ImageAsset(name: "Connectivity/photo.fill.split")
}
public enum Editing {
public static let checkmark20 = ImageAsset(name: "Editing/checkmark.20")
public static let checkmark = ImageAsset(name: "Editing/checkmark")
public static let xmark = ImageAsset(name: "Editing/xmark")
}
@ -128,6 +130,10 @@ public enum Asset {
public enum Discovery {
public static let profileCardBackground = ColorAsset(name: "Scene/Discovery/profile.card.background")
}
public enum Notification {
public static let confirmFollowRequestButtonBackground = ColorAsset(name: "Scene/Notification/confirm.follow.request.button.background")
public static let deleteFollowRequestButtonBackground = ColorAsset(name: "Scene/Notification/delete.follow.request.button.background")
}
public enum Onboarding {
public static let avatarPlaceholder = ImageAsset(name: "Scene/Onboarding/avatar.placeholder")
public static let background = ColorAsset(name: "Scene/Onboarding/background")

View File

@ -178,16 +178,20 @@ extension NotificationView.ViewModel {
if state == .isAccepting {
notificationView.acceptFollowRequestActivityIndicatorView.startAnimating()
notificationView.acceptFollowRequestButton.tintColor = .clear
notificationView.acceptFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.acceptFollowRequestActivityIndicatorView.stopAnimating()
notificationView.acceptFollowRequestButton.tintColor = .white
notificationView.acceptFollowRequestButton.setTitleColor(.white, for: .normal)
}
if state == .isRejecting {
notificationView.rejectFollowRequestActivityIndicatorView.startAnimating()
notificationView.rejectFollowRequestButton.tintColor = .clear
notificationView.rejectFollowRequestButton.setTitleColor(.clear, for: .normal)
} else {
notificationView.rejectFollowRequestActivityIndicatorView.stopAnimating()
notificationView.rejectFollowRequestButton.tintColor = .white
notificationView.rejectFollowRequestButton.tintColor = .black
notificationView.rejectFollowRequestButton.setTitleColor(.black, for: .normal)
}
UIView.animate(withDuration: 0.3) {

View File

@ -110,17 +110,20 @@ public final class NotificationView: UIView {
let acceptFollowRequestButtonShadowBackgroundContainer = ShadowBackgroundContainer()
private(set) lazy var acceptFollowRequestButton: UIButton = {
let button = UIButton()
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.setImage(Asset.Editing.checkmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
let button = HighlightDimmableButton()
button.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
button.setTitleColor(.white, for: .normal)
button.setTitle(L10n.Common.Controls.Actions.confirm, for: .normal)
button.setImage(Asset.Editing.checkmark20.image.withRenderingMode(.alwaysTemplate), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.setBackgroundImage(.placeholder(color: .systemGreen), for: .normal)
button.setBackgroundImage(.placeholder(color: Asset.Scene.Notification.confirmFollowRequestButtonBackground.color), for: .normal)
button.setInsets(forContentPadding: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), imageTitlePadding: 8)
button.tintColor = .white
button.layer.masksToBounds = true
button.layer.cornerCurve = .continuous
button.layer.cornerRadius = 4
button.layer.cornerRadius = 10
button.accessibilityLabel = L10n.Scene.Notification.FollowRequest.accept
acceptFollowRequestButtonShadowBackgroundContainer.cornerRadius = 4
acceptFollowRequestButtonShadowBackgroundContainer.cornerRadius = 10
acceptFollowRequestButtonShadowBackgroundContainer.shadowAlpha = 0.1
button.addTarget(self, action: #selector(NotificationView.acceptFollowRequestButtonDidPressed(_:)), for: .touchUpInside)
return button
@ -129,18 +132,20 @@ public final class NotificationView: UIView {
let rejectFollowRequestButtonShadowBackgroundContainer = ShadowBackgroundContainer()
private(set) lazy var rejectFollowRequestButton: UIButton = {
let button = UIButton()
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold)
button.setImage(Asset.Editing.xmark.image.withRenderingMode(.alwaysTemplate), for: .normal)
let button = HighlightDimmableButton()
button.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
button.setTitleColor(.black, for: .normal)
button.setTitle(L10n.Common.Controls.Actions.delete, for: .normal)
button.setImage(Asset.Circles.forbidden20.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.setInsets(forContentPadding: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), imageTitlePadding: 8)
button.setBackgroundImage(.placeholder(color: Asset.Scene.Notification.deleteFollowRequestButtonBackground.color), for: .normal)
button.tintColor = .black
button.layer.masksToBounds = true
button.layer.cornerCurve = .continuous
button.layer.cornerRadius = 4
button.layer.cornerRadius = 10
button.accessibilityLabel = L10n.Scene.Notification.FollowRequest.reject
rejectFollowRequestButtonShadowBackgroundContainer.cornerRadius = 4
rejectFollowRequestButtonShadowBackgroundContainer.cornerRadius = 10
rejectFollowRequestButtonShadowBackgroundContainer.shadowAlpha = 0.1
button.addTarget(self, action: #selector(NotificationView.rejectFollowRequestButtonDidPressed(_:)), for: .touchUpInside)
return button
@ -303,7 +308,7 @@ extension NotificationView {
followRequestContainerView.axis = .horizontal
followRequestContainerView.distribution = .fillEqually
followRequestContainerView.spacing = 8
followRequestContainerView.spacing = 16
followRequestContainerView.isLayoutMarginsRelativeArrangement = true
followRequestContainerView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 16, right: 0) // set bottom padding
followRequestContainerView.addArrangedSubview(acceptFollowRequestButtonShadowBackgroundContainer)
@ -326,7 +331,7 @@ extension NotificationView {
rejectFollowRequestActivityIndicatorView.centerXAnchor.constraint(equalTo: rejectFollowRequestButton.centerXAnchor),
rejectFollowRequestActivityIndicatorView.centerYAnchor.constraint(equalTo: rejectFollowRequestButton.centerYAnchor),
])
rejectFollowRequestActivityIndicatorView.color = .white
rejectFollowRequestActivityIndicatorView.color = .black
acceptFollowRequestActivityIndicatorView.hidesWhenStopped = true
rejectFollowRequestActivityIndicatorView.stopAnimating()