diff --git a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift index 459e673..6ef86f4 100644 --- a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift +++ b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift @@ -75,6 +75,17 @@ struct ChatDetailView: View { !messageText.isEmpty } + private var sendButtonProgress: CGFloat { + shouldShowSendButton ? 1 : 0 + } + + private var micButtonProgress: CGFloat { + shouldShowSendButton ? 0 : 1 + } + + private var sendButtonWidth: CGFloat { 38 } + private var sendButtonHeight: CGFloat { 36 } + private var composerHorizontalPadding: CGFloat { isInputFocused ? 16 : 28 } @@ -337,7 +348,7 @@ private extension ChatDetailView { .padding(.horizontal, 16) } - HStack(alignment: .bottom, spacing: 6) { + HStack(alignment: .bottom, spacing: shouldShowSendButton ? 0 : 6) { Button { // Placeholder for attachment picker } label: { @@ -366,7 +377,7 @@ private extension ChatDetailView { .padding(.bottom, 8) .frame(maxWidth: .infinity, minHeight: 36, alignment: .bottomLeading) - HStack(alignment: .center, spacing: 8) { + HStack(alignment: .center, spacing: 0) { Button { // Placeholder for quick actions } label: { @@ -381,10 +392,9 @@ private extension ChatDetailView { .accessibilityLabel("Quick actions") .buttonStyle(ChatDetailGlassPressButtonStyle()) } - .padding(.trailing, 8) + .padding(.trailing, 8 + (sendButtonWidth * sendButtonProgress)) .frame(height: 36, alignment: .center) - - if shouldShowSendButton { + .overlay(alignment: .trailing) { Button(action: sendCurrentMessage) { ZStack { // Mirrors the layered blend stack from the original SVG icon. @@ -431,8 +441,10 @@ private extension ChatDetailView { .blendMode(.overlay) } .compositingGroup() + .opacity(0.42 + (0.58 * sendButtonProgress)) + .scaleEffect(0.72 + (0.28 * sendButtonProgress)) .frame(width: 22, height: 19) - .frame(width: 44, height: 36) + .frame(width: sendButtonWidth, height: sendButtonHeight) .background { Capsule().fill(Color(hex: 0x008BFF)) } @@ -440,7 +452,18 @@ private extension ChatDetailView { .accessibilityLabel("Send") .disabled(!canSend) .buttonStyle(ChatDetailGlassPressButtonStyle()) - .transition(.move(edge: .trailing).combined(with: .opacity)) + .allowsHitTesting(shouldShowSendButton) + .opacity(Double(sendButtonProgress)) + .scaleEffect(0.74 + (0.26 * sendButtonProgress), anchor: .trailing) + .blur(radius: (1 - sendButtonProgress) * 2.1) + .mask( + Capsule() + .frame( + width: max(0.001, sendButtonWidth * sendButtonProgress), + height: max(0.001, sendButtonHeight * sendButtonProgress) + ) + .frame(width: sendButtonWidth, height: sendButtonHeight, alignment: .trailing) + ) } } .padding(3) @@ -451,21 +474,28 @@ private extension ChatDetailView { ) } - if !shouldShowSendButton { - Button(action: trailingAction) { - TelegramVectorIcon( - pathData: TelegramIconPath.microphone, - viewBox: CGSize(width: 18, height: 24), - color: Color.white - ) - .frame(width: 18, height: 24) - .frame(width: 42, height: 42) - .background { floatingCircleBackground(strokeOpacity: 0.18) } - } - .accessibilityLabel("Voice message") - .buttonStyle(ChatDetailGlassPressButtonStyle()) - .transition(.move(edge: .trailing).combined(with: .opacity)) + Button(action: trailingAction) { + TelegramVectorIcon( + pathData: TelegramIconPath.microphone, + viewBox: CGSize(width: 18, height: 24), + color: Color.white + ) + .frame(width: 18, height: 24) + .frame(width: 42, height: 42) + .background { floatingCircleBackground(strokeOpacity: 0.18) } } + .accessibilityLabel("Voice message") + .buttonStyle(ChatDetailGlassPressButtonStyle()) + .allowsHitTesting(!shouldShowSendButton) + .opacity(Double(micButtonProgress)) + .scaleEffect( + x: max(0.001, 0.42 + (0.58 * micButtonProgress)), + y: 0.78 + (0.22 * micButtonProgress), + anchor: .trailing + ) + .blur(radius: (1 - micButtonProgress) * 2.4) + .frame(width: 42 * micButtonProgress, height: 42, alignment: .trailing) + .clipped() } .padding(.horizontal, composerHorizontalPadding) .padding(.top, 4)