add button scroll down
This commit is contained in:
parent
b9ea0807e5
commit
b267e5a999
@ -8,6 +8,7 @@ struct PrivateChatView: View {
|
||||
@StateObject private var viewModel: PrivateChatViewModel
|
||||
@State private var hasPositionedToBottom: Bool = false
|
||||
@State private var scrollToBottomTrigger: UUID = .init()
|
||||
@State private var isBottomAnchorVisible: Bool = true
|
||||
@State private var draftText: String = ""
|
||||
@State private var inputTab: ComposerTab = .chat
|
||||
@State private var isVideoPreferred: Bool = false
|
||||
@ -21,26 +22,34 @@ struct PrivateChatView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
content
|
||||
.onChange(of: viewModel.messages.count) { _ in
|
||||
guard !viewModel.isLoadingMore else { return }
|
||||
let targetId = viewModel.messages.last?.id ?? bottomAnchorId
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
proxy.scrollTo(targetId, anchor: .bottom)
|
||||
}
|
||||
hasPositionedToBottom = true
|
||||
}
|
||||
}
|
||||
.onChange(of: scrollToBottomTrigger) { _ in
|
||||
let targetId = viewModel.messages.last?.id ?? bottomAnchorId
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
proxy.scrollTo(targetId, anchor: .bottom)
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
ScrollViewReader { proxy in
|
||||
content
|
||||
.onChange(of: viewModel.messages.count) { _ in
|
||||
guard !viewModel.isLoadingMore else { return }
|
||||
let targetId = viewModel.messages.last?.id ?? bottomAnchorId
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
proxy.scrollTo(targetId, anchor: .bottom)
|
||||
}
|
||||
hasPositionedToBottom = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: scrollToBottomTrigger) { _ in
|
||||
let targetId = viewModel.messages.last?.id ?? bottomAnchorId
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
proxy.scrollTo(targetId, anchor: .bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isBottomAnchorVisible {
|
||||
scrollToBottomButton
|
||||
.padding(.trailing, 20)
|
||||
.padding(.bottom, 80)
|
||||
}
|
||||
}
|
||||
.navigationTitle(title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
@ -99,6 +108,8 @@ struct PrivateChatView: View {
|
||||
Color.clear
|
||||
.frame(height: 1)
|
||||
.id(bottomAnchorId)
|
||||
.onAppear { isBottomAnchorVisible = true }
|
||||
.onDisappear { isBottomAnchorVisible = false }
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
@ -233,9 +244,9 @@ struct PrivateChatView: View {
|
||||
Button(action: { }) { // переключатель на стикеры
|
||||
Image(systemName: "paperclip")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(ComposerIconButtonStyle())
|
||||
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
TextField(inputTab.placeholder, text: $draftText, axis: .vertical)
|
||||
@ -269,10 +280,9 @@ struct PrivateChatView: View {
|
||||
Button(action: { isVideoPreferred.toggle() }) {
|
||||
Image(systemName: isVideoPreferred ? "video.fill" : "mic.fill")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.buttonStyle(ComposerIconButtonStyle())
|
||||
} else {
|
||||
sendButton
|
||||
}
|
||||
@ -284,6 +294,19 @@ struct PrivateChatView: View {
|
||||
.background(.ultraThinMaterial)
|
||||
}
|
||||
|
||||
private var scrollToBottomButton: some View {
|
||||
Button(action: scrollToBottom) {
|
||||
Image(systemName: "arrow.down")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.foregroundColor(.white)
|
||||
.frame(width: 44, height: 44)
|
||||
.background(Color.accentColor)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.shadow(color: Color.black.opacity(0.2), radius: 4, x: 0, y: 2)
|
||||
}
|
||||
|
||||
private var isSendDisabled: Bool {
|
||||
draftText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || currentUserId == nil
|
||||
}
|
||||
@ -346,6 +369,26 @@ struct PrivateChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func scrollToBottom() {
|
||||
scrollToBottomTrigger = .init()
|
||||
hasPositionedToBottom = true
|
||||
isBottomAnchorVisible = true
|
||||
}
|
||||
|
||||
private struct ComposerIconButtonStyle: ButtonStyle {
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.frame(width: 36, height: 36)
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.clipShape(Circle())
|
||||
.overlay(
|
||||
Circle().stroke(Color.secondary.opacity(0.15))
|
||||
)
|
||||
.scaleEffect(configuration.isPressed ? 0.94 : 1)
|
||||
.opacity(configuration.isPressed ? 0.75 : 1)
|
||||
}
|
||||
}
|
||||
|
||||
private enum ComposerTab: String {
|
||||
case chat
|
||||
case stickers
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user