diff --git a/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/ChatBG.png b/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/ChatBG.png new file mode 100644 index 0000000000..92c90ecdf9 Binary files /dev/null and b/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/ChatBG.png differ diff --git a/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/Contents.json b/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/Contents.json new file mode 100644 index 0000000000..9c698680c9 --- /dev/null +++ b/Telegram/WatchApp/tgwatch Watch App/Assets.xcassets/ChatBG.imageset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "filename" : "ChatBG.png", + "idiom" : "universal", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Telegram/WatchApp/tgwatch Watch App/Messages/BubbleStyle.swift b/Telegram/WatchApp/tgwatch Watch App/Messages/BubbleStyle.swift index 3d918ee4b7..c4b49dd5ee 100644 --- a/Telegram/WatchApp/tgwatch Watch App/Messages/BubbleStyle.swift +++ b/Telegram/WatchApp/tgwatch Watch App/Messages/BubbleStyle.swift @@ -1,9 +1,8 @@ import SwiftUI -/// Resolved colors for a message-bubble surface. Incoming bubbles are a fixed light (white) -/// surface with dark content; outgoing are the accent surface with white content. Colors are -/// FIXED (non-adaptive) on purpose: watchOS runs this app in permanent dark mode, so `.primary` -/// would resolve to white and vanish on the white incoming surface. +/// Resolved colors for a message-bubble surface. Both directions use a dark fixed surface +/// with white content. Colors are FIXED (non-adaptive) on purpose: watchOS runs this app in +/// permanent dark mode. struct BubbleStyle: Equatable { let fill: Color let content: Color @@ -13,15 +12,15 @@ struct BubbleStyle: Equatable { let playIcon: Color static let incoming = BubbleStyle( - fill: .white, - content: .black, - secondary: Color.black.opacity(0.5), + fill: Color(red: 40 / 255, green: 40 / 255, blue: 40 / 255), + content: .white, + secondary: Color.white.opacity(0.7), replyBar: .accentColor, playFill: .accentColor, playIcon: .white ) static let outgoing = BubbleStyle( - fill: .accentColor, + fill: Color(red: 19 / 255, green: 44 / 255, blue: 73 / 255), content: .white, secondary: Color.white.opacity(0.7), replyBar: Color.white.opacity(0.7), diff --git a/Telegram/WatchApp/tgwatch Watch App/Messages/ComposeSheet.swift b/Telegram/WatchApp/tgwatch Watch App/Messages/ComposeSheet.swift deleted file mode 100644 index 03fe0b6e5d..0000000000 --- a/Telegram/WatchApp/tgwatch Watch App/Messages/ComposeSheet.swift +++ /dev/null @@ -1,58 +0,0 @@ -import SwiftUI - -struct ComposeSheet: View { - let initialText: String - let onSend: (String) async -> Bool - let onDismissWithDraft: (String) async -> Void - - @State private var text: String = "" - @State private var sending: Bool = false - @FocusState private var focused: Bool - @Environment(\.dismiss) private var dismiss - - var body: some View { - ScrollView { - VStack(spacing: 8) { - TextField("Reply…", text: $text, axis: .vertical) - .focused($focused) - .accessibilityIdentifier("composeField") - Button(action: send) { - if sending { - ProgressView() - } else { - Label("Send", systemImage: "paperplane.fill") - } - } - .buttonStyle(.borderedProminent) - .disabled(sending || text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) - .accessibilityIdentifier("composeSend") - } - .padding(8) - } - .task { - text = initialText - focused = true - } - .onDisappear { - // If we're sending, the dismiss came from send() — skip the draft write. - // The send call has clearDraft: true server-side, so the draft is already cleared. - guard !sending else { return } - let snapshot = text - Task { await onDismissWithDraft(snapshot) } - } - } - - private func send() { - let snapshot = text.trimmingCharacters(in: .whitespacesAndNewlines) - guard !snapshot.isEmpty else { return } - sending = true - Task { - let success = await onSend(snapshot) - if success { - dismiss() - } else { - sending = false - } - } - } -} diff --git a/Telegram/WatchApp/tgwatch Watch App/Messages/MessageListView.swift b/Telegram/WatchApp/tgwatch Watch App/Messages/MessageListView.swift index cd691449f0..9f7e45b1a8 100644 --- a/Telegram/WatchApp/tgwatch Watch App/Messages/MessageListView.swift +++ b/Telegram/WatchApp/tgwatch Watch App/Messages/MessageListView.swift @@ -16,7 +16,6 @@ struct MessageListView: View { @State private var presentedVideo: VideoVisual? @State private var presentedVideoNote: VideoNoteVisual? @State private var presentedPoll: PollVoteTarget? - @State private var showCompose: Bool = false @State private var showAttachment: Bool = false @State private var stickerPickerStore: StickerPickerStore? // True when the user is parked within slop of the bottom edge. Updated only on @@ -79,18 +78,6 @@ struct MessageListView: View { ) } } - .sheet(isPresented: $showCompose) { - if let store { - ComposeSheet( - initialText: store.draftText, - onSend: { text in - await store.sendText(text) - return store.lastSendError == nil - }, - onDismissWithDraft: { text in await store.saveDraft(text) } - ) - } - } .sheet(isPresented: $showAttachment) { if let store { AttachmentSheet( @@ -243,7 +230,9 @@ struct MessageListView: View { if row.canSend { ReplyBar( onAttachTap: { showAttachment = true }, - onTextTap: { showCompose = true } + onSend: { snapshot in + Task { await store.sendText(snapshot) } + } ) .id("composeAnchor") .padding(.top, 8) @@ -261,6 +250,14 @@ struct MessageListView: View { } .ignoresSafeArea(edges: .bottom) .defaultScrollAnchor(.bottom) + .scrollContentBackground(.hidden) + .background { + Image("ChatBG") + .resizable() + .aspectRatio(contentMode: .fill) + .clipped() + .ignoresSafeArea() + } .environment(store) .task { // Default branch only appears once `loadState == .loaded` diff --git a/Telegram/WatchApp/tgwatch Watch App/Messages/ReplyBar.swift b/Telegram/WatchApp/tgwatch Watch App/Messages/ReplyBar.swift index 8e19ccbb47..19e5db6b19 100644 --- a/Telegram/WatchApp/tgwatch Watch App/Messages/ReplyBar.swift +++ b/Telegram/WatchApp/tgwatch Watch App/Messages/ReplyBar.swift @@ -2,7 +2,7 @@ import SwiftUI struct ReplyBar: View { let onAttachTap: () -> Void - let onTextTap: () -> Void + let onSend: (String) -> Void // Circular "+" button and reply-field capsule share a single pill // diameter so they line up vertically and visually mirror the @@ -25,17 +25,31 @@ struct ReplyBar: View { .padding(.leading, 11) .accessibilityIdentifier("replyBarAttach") - Button(action: onTextTap) { + // TextFieldLink renders an arbitrary label and, when tapped, + // presents the watchOS system text input UI (QuickboardViewService). + // It returns the committed string in onSubmit. The label IS our + // glassy pill — fully styled, no double-chrome artifacts. + // + // Limitation: TextFieldLink does not accept an initial value, so + // the input UI opens blank every time. Existing server-side drafts + // are not visible or editable from the watch under this design — + // accepted regression vs. the prior ComposeSheet flow. + TextFieldLink(prompt: Text("Reply…")) { HStack(spacing: 0) { Text("Reply…") .font(.caption) .foregroundStyle(.secondary) + .lineLimit(1) Spacer(minLength: 0) } .padding(.horizontal, 12) .frame(height: Self.pillHeight) .contentShape(Rectangle()) .glassEffect() + } onSubmit: { newText in + let snapshot = newText.trimmingCharacters(in: .whitespacesAndNewlines) + guard !snapshot.isEmpty else { return } + onSend(snapshot) } .buttonStyle(.plain) .layoutPriority(1) @@ -64,7 +78,7 @@ private struct ReplyBarGeometryPreviewHost: View { .padding(.top, 6) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - ReplyBar(onAttachTap: {}, onTextTap: {}) + ReplyBar(onAttachTap: {}, onSend: { _ in }) .padding(.horizontal, 4) .padding(.bottom, 19) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) diff --git a/Telegram/WatchApp/tgwatch.xcodeproj/project.pbxproj b/Telegram/WatchApp/tgwatch.xcodeproj/project.pbxproj index ab950ef6f3..9b0fe1557e 100644 --- a/Telegram/WatchApp/tgwatch.xcodeproj/project.pbxproj +++ b/Telegram/WatchApp/tgwatch.xcodeproj/project.pbxproj @@ -75,7 +75,6 @@ A0899809BBC92D1563AC4160 /* ReplyBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7584BF25A84BAD24AB992A79 /* ReplyBar.swift */; }; A121DA7622C910B267D69B6E /* PhotoBubbleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7533C5A9B419B29727C86 /* PhotoBubbleView.swift */; }; A6179A26CB7FB298567682E6 /* DocumentVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2515F0EDA29D5270610A69 /* DocumentVisual.swift */; }; - A73233E40B531EA2D4A81B78 /* ComposeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54149DBDF74BC221A94FB1AD /* ComposeSheet.swift */; }; A7967495A54936FE19218DC4 /* ServiceMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF393D904BF677AC5721EC7E /* ServiceMessageView.swift */; }; A8EF6F72C78B619893C05AFE /* ChatPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448938C1F69F0D9DE679238 /* ChatPreview.swift */; }; AA3F067A7BC4CDA579CFFC39 /* PollVisual.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4D691106D7EA2FEE35F37F /* PollVisual.swift */; }; @@ -149,7 +148,6 @@ 4DB3C8238D41374CBAC66303 /* EmojiText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiText.swift; sourceTree = ""; }; 4E049ECC956A583A92C5C7CB /* SecretsValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretsValidator.swift; sourceTree = ""; }; 53B9831274FA56126E3F5A42 /* LocationSendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSendView.swift; sourceTree = ""; }; - 54149DBDF74BC221A94FB1AD /* ComposeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeSheet.swift; sourceTree = ""; }; 596AF8BF88C38D2018397A33 /* LocationBubbleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationBubbleView.swift; sourceTree = ""; }; 5D3A8FBA5EBDBFDE5AA2FFEC /* QrLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrLoginView.swift; sourceTree = ""; }; 5EA750417DF3AD62C7B788F6 /* MessageListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView.swift; sourceTree = ""; }; @@ -340,7 +338,6 @@ 3EEAE6237003C5CDA788E928 /* BubbleStyle.swift */, C2C84B842AD08D079BFF08A0 /* ChatHistoryLoader.swift */, 182DA99AD90B93DE2FB37A41 /* ChatHistoryStore.swift */, - 54149DBDF74BC221A94FB1AD /* ComposeSheet.swift */, E855E4C62401AD1CF41D1835 /* DaySeparatorView.swift */, BDBE08BBB88597B10CDF0142 /* DocumentBubbleView.swift */, FF2515F0EDA29D5270610A69 /* DocumentVisual.swift */, @@ -536,7 +533,6 @@ E48523AFA2E23409CC0C2C2C /* ChatRow.swift in Sources */, 7BF1C89F67290EBAF50900F8 /* ChatRowView.swift in Sources */, 04CEF3F9C267D8A7CEC5EC3C /* ClosedView.swift in Sources */, - A73233E40B531EA2D4A81B78 /* ComposeSheet.swift in Sources */, EF499261BE49A6B1C7B3B1F4 /* ContentView.swift in Sources */, FD5515DB22C571F4D3E5F5FA /* DaySeparatorView.swift in Sources */, F5596A4EF4D2055B10B4A183 /* DocumentBubbleView.swift in Sources */,