tls sasl
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

This commit is contained in:
wt committed 2026-09-02 11:46:11 +07:00
1 parent e3fcee3351
commit d4b4c92759
29 files changed
+2177 -61

No files matched your search

@@ -53,6 +53,16 @@ struct AudioMessagePlayer: View {
.font(.caption2.monospacedDigit())
.foregroundStyle(secondaryText)
Spacer()
Button(action: saveToFiles) {
Image(systemName: "square.and.arrow.down")
.font(.caption.weight(.bold))
.foregroundStyle(accent)
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(accent.opacity(0.14), in: Capsule())
}
.buttonStyle(.plain)
.accessibilityLabel("Сохранить в Файлы")
Button(action: playback.cycleVoiceRate) {
Text(rateText)
.font(.caption2.monospacedDigit().weight(.bold))
@@ -95,6 +105,13 @@ struct AudioMessagePlayer: View {
countStyle: .file
))
}
Spacer()
Button(action: saveToFiles) {
Image(systemName: "square.and.arrow.down")
.foregroundStyle(accent)
}
.buttonStyle(.plain)
.accessibilityLabel("Сохранить в Файлы")
}
.font(.caption2.monospacedDigit())
.foregroundStyle(secondaryText)
@@ -185,6 +202,10 @@ struct AudioMessagePlayer: View {
message.direction == .outgoing ? .white.opacity(0.78) : .secondary
}
private func saveToFiles() {
Task { await MediaDownloadService.save(message, model: model) }
}
private func togglePlayback() {
guard let localURL else {
Task {
@@ -45,6 +45,7 @@ struct MediaViewer: View {
@State private var isTrackingDismissGesture = false
@State private var isCompletingDismissal = false
@State private var isPhotoZoomed = false
@State private var saveErrorMessage: String?
var body: some View {
GeometryReader { geometry in
@@ -65,6 +66,16 @@ struct MediaViewer: View {
.lineLimit(1)
Spacer()
Button(action: save) {
Image(systemName: "square.and.arrow.down")
.font(.system(size: 17, weight: .semibold))
.foregroundStyle(.white)
.frame(width: 36, height: 36)
.background(.white.opacity(0.16), in: Circle())
}
.buttonStyle(.plain)
.accessibilityLabel("Сохранить")
}
.padding(.horizontal, 16)
.padding(.top, 12)
@@ -96,6 +107,11 @@ struct MediaViewer: View {
.accessibilityAction(.escape) {
closeImmediately()
}
.alert("Ошибка сохранения", isPresented: saveErrorBinding) {
Button("OK", role: .cancel) {}
} message: {
Text(saveErrorMessage ?? "Не удалось сохранить медиа.")
}
}
#if os(macOS)
.onExitCommand {
@@ -183,6 +199,27 @@ struct MediaViewer: View {
onClose()
}
private var saveErrorBinding: Binding<Bool> {
Binding(
get: { saveErrorMessage != nil },
set: { visible in if !visible { saveErrorMessage = nil } }
)
}
private func save() {
Task {
do {
try await MediaDownloadService.save(
url: item.url,
kind: item.kind,
filename: item.title
)
} catch {
saveErrorMessage = error.localizedDescription
}
}
}
private func dismissalProgress(in containerHeight: CGFloat) -> CGFloat {
min(1, abs(dragOffset) / max(1, containerHeight * 0.62))
}
@@ -81,6 +81,13 @@ struct MessageBubble: View {
Label("Копировать текст", systemImage: "doc.on.doc")
}
}
if message.kind.isMedia, !message.isRetracted {
Button {
Task { await MediaDownloadService.save(message, model: model) }
} label: {
Label("Сохранить", systemImage: "square.and.arrow.down")
}
}
if model.canRetryMediaMessage(message) {
Button(action: onRetry) {
Label("Повторить отправку", systemImage: "arrow.clockwise")