Update tgwatch

This commit is contained in:
isaac 2026-05-28 19:00:35 +02:00
parent 8f4f463265
commit 045ba10386
7 changed files with 48 additions and 87 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -0,0 +1,13 @@
{
"images" : [
{
"filename" : "ChatBG.png",
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -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),

View file

@ -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
}
}
}
}

View file

@ -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`

View file

@ -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)

View file

@ -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 = "<group>"; };
4E049ECC956A583A92C5C7CB /* SecretsValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretsValidator.swift; sourceTree = "<group>"; };
53B9831274FA56126E3F5A42 /* LocationSendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSendView.swift; sourceTree = "<group>"; };
54149DBDF74BC221A94FB1AD /* ComposeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeSheet.swift; sourceTree = "<group>"; };
596AF8BF88C38D2018397A33 /* LocationBubbleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationBubbleView.swift; sourceTree = "<group>"; };
5D3A8FBA5EBDBFDE5AA2FFEC /* QrLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrLoginView.swift; sourceTree = "<group>"; };
5EA750417DF3AD62C7B788F6 /* MessageListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView.swift; sourceTree = "<group>"; };
@ -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 */,