From 1ccb4aeb13b58fcc75e8709b9614484babcabec2 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 9 Jun 2022 16:41:54 +0800 Subject: [PATCH] feat: add follows you indicator. resolve #397 --- Localization/app.json | 3 ++ .../xcschemes/xcschememanagement.plist | 12 ++--- .../View/ProfileHeaderView+ViewModel.swift | 9 +++- .../Header/View/ProfileHeaderView.swift | 48 +++++++++++++++++-- .../Scene/Profile/ProfileViewController.swift | 16 +++++++ 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Localization/app.json b/Localization/app.json index 2a8634a67..93c3f9955 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -417,6 +417,9 @@ } }, "profile": { + "header": { + "follows_you": "Follows You" + }, "dashboard": { "posts": "posts", "following": "following", diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 1c922a0b5..10b782b15 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -24,17 +24,17 @@ Mastodon - RTL.xcscheme_^#shared#^_ orderHint - 11 + 12 Mastodon - Release.xcscheme_^#shared#^_ orderHint - 4 + 5 Mastodon - Snapshot.xcscheme_^#shared#^_ orderHint - 6 + 7 Mastodon - ar.xcscheme @@ -114,7 +114,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 30 + 23 MastodonIntents.xcscheme_^#shared#^_ @@ -129,12 +129,12 @@ NotificationService.xcscheme_^#shared#^_ orderHint - 29 + 24 ShareActionExtension.xcscheme_^#shared#^_ orderHint - 31 + 22 SuppressBuildableAutocreation diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift index 808e1d7ba..b57bf95a5 100644 --- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift +++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift @@ -88,6 +88,13 @@ extension ProfileHeaderView.ViewModel { ) } .store(in: &disposeBag) + // follows you + $relationshipActionOptionSet + .map { $0.contains(.followingBy) && !$0.contains(.isMyself) } + .sink { isFollowingBy in + view.followsYouBlurEffectView.isHidden = !isFollowingBy + } + .store(in: &disposeBag) // avatar Publishers.CombineLatest4( $avatarImageURL, @@ -102,7 +109,7 @@ extension ProfileHeaderView.ViewModel { )) } .store(in: &disposeBag) - // blur + // blur for blocking & blockingBy $relationshipActionOptionSet .map { $0.contains(.blocking) || $0.contains(.blockingBy) } .sink { needsImageOverlayBlurred in diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift index 7257333d0..d99b90bc4 100644 --- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift +++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift @@ -71,6 +71,16 @@ final class ProfileHeaderView: UIView { }() var bannerImageViewTopLayoutConstraint: NSLayoutConstraint! var bannerImageViewBottomLayoutConstraint: NSLayoutConstraint! + + let followsYouBlurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular)) + let followsYouVibrantEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .regular), style: .label)) + let followsYouLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 15, weight: .regular) + label.text = "Follows You" // TODO: i18n + return label + }() + let followsYouMaskView = UIView() let avatarImageViewBackgroundView: UIView = { let view = UIView() @@ -173,9 +183,6 @@ final class ProfileHeaderView: UIView { button.titleLabel?.minimumScaleFactor = 0.5 return button }() - - // let bioContainerView = UIView() - // let fieldContainerStackView = UIStackView() let bioMetaText: MetaText = { let metaText = MetaText() @@ -262,7 +269,41 @@ extension ProfileHeaderView { bannerImageViewOverlayVisualEffectView.trailingAnchor.constraint(equalTo: bannerImageView.trailingAnchor), bannerImageViewOverlayVisualEffectView.bottomAnchor.constraint(equalTo: bannerImageView.bottomAnchor), ]) + + // follows you + followsYouBlurEffectView.translatesAutoresizingMaskIntoConstraints = false + addSubview(followsYouBlurEffectView) + NSLayoutConstraint.activate([ + layoutMarginsGuide.trailingAnchor.constraint(equalTo: followsYouBlurEffectView.trailingAnchor), + bannerContainerView.bottomAnchor.constraint(equalTo: followsYouBlurEffectView.bottomAnchor, constant: 16), + ]) + followsYouBlurEffectView.layer.masksToBounds = true + followsYouBlurEffectView.layer.cornerRadius = 8 + followsYouBlurEffectView.layer.cornerCurve = .continuous + followsYouBlurEffectView.isHidden = true + followsYouVibrantEffectView.translatesAutoresizingMaskIntoConstraints = false + followsYouBlurEffectView.contentView.addSubview(followsYouVibrantEffectView) + NSLayoutConstraint.activate([ + followsYouVibrantEffectView.topAnchor.constraint(equalTo: followsYouBlurEffectView.topAnchor), + followsYouVibrantEffectView.leadingAnchor.constraint(equalTo: followsYouBlurEffectView.leadingAnchor), + followsYouVibrantEffectView.trailingAnchor.constraint(equalTo: followsYouBlurEffectView.trailingAnchor), + followsYouVibrantEffectView.bottomAnchor.constraint(equalTo: followsYouBlurEffectView.bottomAnchor), + ]) + + followsYouLabel.translatesAutoresizingMaskIntoConstraints = false + followsYouVibrantEffectView.contentView.addSubview(followsYouLabel) + NSLayoutConstraint.activate([ + followsYouLabel.topAnchor.constraint(equalTo: followsYouVibrantEffectView.topAnchor, constant: 4), + followsYouLabel.leadingAnchor.constraint(equalTo: followsYouVibrantEffectView.leadingAnchor, constant: 6), + followsYouVibrantEffectView.trailingAnchor.constraint(equalTo: followsYouLabel.trailingAnchor, constant: 6), + followsYouVibrantEffectView.bottomAnchor.constraint(equalTo: followsYouLabel.bottomAnchor, constant: 4), + ]) + + followsYouMaskView.frame = CGRect(x: 0, y: 0, width: 1000, height: 1000) + followsYouMaskView.backgroundColor = .red + followsYouBlurEffectView.mask = followsYouMaskView + // avatar avatarImageViewBackgroundView.translatesAutoresizingMaskIntoConstraints = false addSubview(avatarImageViewBackgroundView) @@ -406,6 +447,7 @@ extension ProfileHeaderView { container.addArrangedSubview(bioMetaText.textView) bringSubviewToFront(bannerContainerView) + bringSubviewToFront(followsYouBlurEffectView) bringSubviewToFront(avatarImageViewBackgroundView) statusDashboardView.delegate = self diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 6a51ff599..4c77a4458 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -584,6 +584,22 @@ extension ProfileViewController: TabBarPagerDelegate { progress = 0 } + // setup follows you mask + // 1. set mask size + profileHeaderViewController.profileHeaderView.followsYouMaskView.frame = profileHeaderViewController.profileHeaderView.followsYouBlurEffectView.bounds + // 2. check follows you view overflow navigation bar or not + let followsYouBlurEffectViewInWindow = profileHeaderViewController.profileHeaderView.convert( + profileHeaderViewController.profileHeaderView.followsYouBlurEffectView.frame, + to: nil + ) + if followsYouBlurEffectViewInWindow.minY < tabBarPagerController.containerScrollView.safeAreaInsets.top { + let offestY = tabBarPagerController.containerScrollView.safeAreaInsets.top - followsYouBlurEffectViewInWindow.minY + let height = profileHeaderViewController.profileHeaderView.followsYouMaskView.frame.height + profileHeaderViewController.profileHeaderView.followsYouMaskView.frame.origin.y = min(offestY, height) + } else { + profileHeaderViewController.profileHeaderView.followsYouMaskView.frame.origin.y = .zero + } + // setup titleView offset and fade avatar profileHeaderViewController.updateHeaderScrollProgress(progress, throttle: throttle)