Пуш-нотификации, кросс-платформенный аудит + 65 новых тестов (badge, dedup, Desktop-suppression, payload parity)
This commit is contained in:
@@ -255,6 +255,14 @@ struct ChatDetailView: View {
|
||||
pendingGroupInviteTitle = parsed.title
|
||||
}
|
||||
}
|
||||
cellActions.onGroupInviteOpen = { dialogKey in
|
||||
let title = DialogRepository.shared.dialogs[dialogKey]?.opponentTitle ?? "Group"
|
||||
let route = ChatRoute(groupDialogKey: dialogKey, title: title)
|
||||
NotificationCenter.default.post(
|
||||
name: .openChatFromNotification,
|
||||
object: route
|
||||
)
|
||||
}
|
||||
// Capture first unread incoming message BEFORE marking as read.
|
||||
if firstUnreadMessageId == nil {
|
||||
firstUnreadMessageId = messages.first(where: {
|
||||
|
||||
@@ -450,9 +450,10 @@ struct ImageGalleryViewer: View {
|
||||
|
||||
// MARK: - GalleryPageModifier
|
||||
|
||||
/// Applies hero transition frame/offset ONLY for the initial page.
|
||||
/// Non-hero pages have NO explicit frame — they fill the TabView page naturally,
|
||||
/// which fixes the "tiny image" bug caused by explicit frame fighting with TabView sizing.
|
||||
/// Applies hero transition frame/offset for the initial page.
|
||||
/// Uses a SINGLE view branch (no if/else) to preserve SwiftUI structural identity
|
||||
/// across the hero → expanded transition. This prevents GeometryReader inside
|
||||
/// ZoomableImagePage from receiving stale/incorrect proposed sizes during the swap.
|
||||
private struct GalleryPageModifier: ViewModifier {
|
||||
let heroActive: Bool
|
||||
let sourceFrame: CGRect
|
||||
@@ -460,16 +461,17 @@ private struct GalleryPageModifier: ViewModifier {
|
||||
let dragOffset: CGSize
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if heroActive {
|
||||
content
|
||||
.frame(width: sourceFrame.width, height: sourceFrame.height)
|
||||
.clipped()
|
||||
.offset(x: sourceFrame.minX, y: sourceFrame.minY)
|
||||
.offset(dragOffset)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
} else {
|
||||
content
|
||||
.offset(dragOffset)
|
||||
}
|
||||
content
|
||||
.frame(
|
||||
width: heroActive ? sourceFrame.width : fullSize.width,
|
||||
height: heroActive ? sourceFrame.height : fullSize.height
|
||||
)
|
||||
.clipped()
|
||||
.offset(
|
||||
x: heroActive ? sourceFrame.minX : 0,
|
||||
y: heroActive ? sourceFrame.minY : 0
|
||||
)
|
||||
.offset(dragOffset)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ struct ZoomableImagePage: View {
|
||||
.position(x: viewSize.width / 2, y: viewSize.height / 2)
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture(count: 2) {
|
||||
withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) {
|
||||
|
||||
Reference in New Issue
Block a user