Pin details polish

This commit is contained in:
kate-signal 2025-12-22 15:17:28 -05:00 committed by GitHub
parent 1db9e435cf
commit 6fb8b566b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 6 deletions

View File

@ -144,11 +144,15 @@ public class CVComponentDateHeader: CVComponentBase, CVRootComponent {
return contentView.rootView
}
let isStandaloneRenderItem = conversationStyle.isStandaloneRenderItem
// On iOS 26 always use `visual effect` content view for the sticky header.
if componentDelegate.isConversationPreview {
return buildVisualEffectContentView()
} else if hasWallpaper, #unavailable(iOS 26) {
return buildPlainContentView()
} else if isStandaloneRenderItem {
return buildPlainContentView()
} else {
let plainContentView = buildPlainContentView()
let visualEffectContentView = buildVisualEffectContentView()

View File

@ -127,7 +127,7 @@ extension ConversationViewController {
private class ConversationBannerView: UIView {
internal var contentView: UIView & UIContentView
private var blurBackgroundView: UIVisualEffectView?
var blurBackgroundView: UIVisualEffectView?
public static func fadeInAnimator() -> UIViewPropertyAnimator {
return UIViewPropertyAnimator(
@ -1111,8 +1111,9 @@ internal extension ConversationViewController {
)
let banner = ConversationBannerView(configuration: bannerConfiguration)
let longPressInteraction = UIContextMenuInteraction(delegate: self)
banner.addInteraction(longPressInteraction)
banner.blurBackgroundView?.addInteraction(longPressInteraction)
// Set up interaction delegate for pin icon menu
banner.pinnedMessageDelegate = self

View File

@ -484,6 +484,30 @@ extension ConversationViewController: MessageActionsDelegate {
}
}
public func handleActionUnpinAsync(message: TSMessage) async {
let pinnedMessageManager = DependenciesBridge.shared.pinnedMessageManager
let db = DependenciesBridge.shared.db
let unpinMessage = db.write { tx in
pinnedMessageManager.getOutgoingUnpinMessage(
interaction: message,
thread: thread,
expiresAt: nil,
tx: tx
)
}
guard let unpinMessage else {
return
}
await queuePinMessageChangeWithModal(
message: message,
pinMessage: unpinMessage,
completion: nil
)
}
func messageActionsChangePinStatus(_ itemViewModel: CVItemViewModelImpl, pin: Bool) {
guard let message = itemViewModel.renderItem.interaction as? TSMessage else {
return

View File

@ -18,6 +18,9 @@ protocol PinnedMessageInteractionManagerDelegate: AnyObject {
/// Presents the "see all messages" details view.
func presentSeeAllMessages()
/// Unpins all messages
func unpinAllMessages()
}
public struct PinnedMessageBannerData {
@ -355,4 +358,18 @@ extension ConversationViewController: PinnedMessageInteractionManagerDelegate {
pmDetailsController.modalPresentationStyle = .pageSheet
present(pmDetailsController, animated: true)
}
func unpinAllMessages() {
Task {
for message in threadViewModel.pinnedMessages {
await handleActionUnpinAsync(message: message)
}
presentToast(
text: OWSLocalizedString(
"PINNED_MESSAGE_TOAST",
comment: "Text to show on a toast when someone unpins a message"
)
)
}
}
}

View File

@ -57,6 +57,7 @@ class PinnedMessagesDetailsViewController: OWSViewController, DatabaseChangeDele
titleStackView.spacing = 4
navigationItem.titleView = titleStackView
navigationItem.rightBarButtonItem = .doneButton(dismissingFrom: self)
}
private func layoutPinnedMessages(tx: DBReadTransaction) {
@ -112,7 +113,8 @@ class PinnedMessagesDetailsViewController: OWSViewController, DatabaseChangeDele
scrollView.autoPinEdgesToSuperviewEdges()
paddedContainerView.autoPinEdgesToSuperviewEdges()
stack.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16))
stack.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 16, left: 16, bottom: 64, right: 16))
paddedContainerView.autoMatch(.width, to: .width, of: scrollView)
}
@ -134,6 +136,23 @@ class PinnedMessagesDetailsViewController: OWSViewController, DatabaseChangeDele
db.read { tx in
layoutPinnedMessages(tx: tx)
}
let unpinAllButton = UIButton(
configuration: .largeSecondary(title: OWSLocalizedString(
"PINNED_MESSAGES_UNPIN_ALL",
comment: "Title for a button to unpin all pinned messages."
)),
primaryAction: UIAction { [weak self] _ in
self?.dismiss(animated: true)
self?.delegate?.unpinAllMessages()
},
)
view.addSubview(unpinAllButton)
unpinAllButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
unpinAllButton.bottomAnchor.constraint(equalTo: contentLayoutGuide.bottomAnchor),
unpinAllButton.centerXAnchor.constraint(equalTo: contentLayoutGuide.centerXAnchor),
])
}
private func buildButtonAndCellStack(renderItem: CVRenderItem, message: TSMessage, reversedIndex: Int) -> UIStackView {
@ -192,7 +211,8 @@ class PinnedMessagesDetailsViewController: OWSViewController, DatabaseChangeDele
chatColor: DependenciesBridge.shared.chatColorSettingStore.resolvedChatColor(
for: threadViewModel.threadRecord,
tx: tx
)
),
isStandaloneRenderItem: true
)
return CVLoader.buildStandaloneRenderItem(
@ -521,7 +541,7 @@ extension PinnedMessagesDetailsViewController: CVComponentDelegate {
return {}
}
var isConversationPreview: Bool { true }
var isConversationPreview: Bool { false }
var wallpaperBlurProvider: WallpaperBlurProvider? { nil }

View File

@ -6616,6 +6616,9 @@
/* Action menu item to unpin a message */
"PINNED_MESSAGES_UNPIN" = "Unpin";
/* Title for a button to unpin all pinned messages. */
"PINNED_MESSAGES_UNPIN_ALL" = "Unpin All";
/* The title for pinned conversation section on the conversation list */
"PINNED_SECTION_TITLE" = "Pinned";

View File

@ -38,6 +38,8 @@ public struct ConversationStyle {
public let isWallpaperPhoto: Bool
public let isStandaloneRenderItem: Bool
private let dynamicBodyTypePointSize: CGFloat
private let primaryTextColor: UIColor
@ -109,7 +111,8 @@ public struct ConversationStyle {
viewWidth: CGFloat,
hasWallpaper: Bool,
isWallpaperPhoto: Bool,
chatColor: ColorOrGradientSetting
chatColor: ColorOrGradientSetting,
isStandaloneRenderItem: Bool = false
) {
self.type = type
self.viewWidth = viewWidth
@ -169,6 +172,8 @@ public struct ConversationStyle {
let kMaxAudioMessageWidth: CGFloat = 244
maxAudioMessageWidth = floor(min(maxMessageWidth, kMaxAudioMessageWidth))
self.isStandaloneRenderItem = isStandaloneRenderItem
}
// MARK: Colors