119 lines
4.0 KiB
Swift
119 lines
4.0 KiB
Swift
//
|
|
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
extension ConversationViewController {
|
|
func checkPermissionsAndStartRecordingVoiceMessage() {
|
|
AssertIsOnMainThread()
|
|
|
|
// Cancel any ongoing audio playback.
|
|
cvAudioPlayer.stopAll()
|
|
|
|
let voiceMessageModel = VoiceMessageModel(thread: thread)
|
|
viewState.currentVoiceMessageModel = voiceMessageModel
|
|
|
|
// Delay showing the voice memo UI for N ms to avoid a jarring transition
|
|
// when you just tap and don't hold.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
guard let self = self else { return }
|
|
guard self.viewState.currentVoiceMessageModel === voiceMessageModel else { return }
|
|
self.configureScrollDownButtons()
|
|
self.inputToolbar?.showVoiceMemoUI()
|
|
}
|
|
|
|
ows_askForMicrophonePermissions { [weak self] granted in
|
|
guard let self = self else { return }
|
|
guard self.viewState.currentVoiceMessageModel === voiceMessageModel else { return }
|
|
|
|
guard granted else {
|
|
self.cancelRecordingVoiceMessage()
|
|
self.ows_showNoMicrophonePermissionActionSheet()
|
|
return
|
|
}
|
|
|
|
self.startRecordingVoiceMessage()
|
|
}
|
|
}
|
|
|
|
private func startRecordingVoiceMessage() {
|
|
AssertIsOnMainThread()
|
|
|
|
guard let voiceMessageModel = viewState.currentVoiceMessageModel else {
|
|
return owsFailDebug("Unexpectedly missing voice message model")
|
|
}
|
|
|
|
ImpactHapticFeedback.impactOccured(style: .light)
|
|
|
|
do {
|
|
try voiceMessageModel.startRecording()
|
|
} catch {
|
|
owsFailDebug("Failed to start recording voice message \(error)")
|
|
cancelRecordingVoiceMessage()
|
|
}
|
|
}
|
|
|
|
func cancelRecordingVoiceMessage() {
|
|
AssertIsOnMainThread()
|
|
|
|
defer { viewState.currentVoiceMessageModel = nil }
|
|
guard let voiceMessageModel = viewState.currentVoiceMessageModel else { return }
|
|
|
|
voiceMessageModel.stopRecordingAsync()
|
|
|
|
NotificationHapticFeedback().notificationOccurred(.warning)
|
|
|
|
clearVoiceMessageDraft()
|
|
viewState.currentVoiceMessageModel = nil
|
|
inputToolbar?.hideVoiceMemoUI(true)
|
|
configureScrollDownButtons()
|
|
}
|
|
|
|
private static let minimumVoiceMessageDuration: TimeInterval = 1
|
|
|
|
@objc(finishRecordingVoiceMessageAndSendImmediately:)
|
|
func finishRecordingVoiceMessage(sendImmediately: Bool = false) {
|
|
AssertIsOnMainThread()
|
|
|
|
defer { viewState.currentVoiceMessageModel = nil }
|
|
guard let voiceMessageModel = viewState.currentVoiceMessageModel else { return }
|
|
|
|
voiceMessageModel.stopRecording()
|
|
|
|
guard let duration = voiceMessageModel.duration, duration >= Self.minimumVoiceMessageDuration else {
|
|
inputToolbar?.showVoiceMemoTooltip()
|
|
cancelRecordingVoiceMessage()
|
|
return
|
|
}
|
|
|
|
ImpactHapticFeedback.impactOccured(style: .medium)
|
|
|
|
if sendImmediately {
|
|
sendVoiceMessageModel(voiceMessageModel)
|
|
} else {
|
|
databaseStorage.asyncWrite { voiceMessageModel.saveDraft(transaction: $0) } completion: {
|
|
self.inputToolbar?.showVoiceMemoDraft(voiceMessageModel)
|
|
self.configureScrollDownButtons()
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendVoiceMessageModel(_ voiceMessageModel: VoiceMessageModel) {
|
|
inputToolbar?.hideVoiceMemoUI(true)
|
|
configureScrollDownButtons()
|
|
|
|
do {
|
|
let attachment = try voiceMessageModel.prepareForSending()
|
|
tryToSendAttachments([attachment], messageBody: nil)
|
|
clearVoiceMessageDraft()
|
|
} catch {
|
|
owsFailDebug("Failed to send prepare voice message for sending \(error)")
|
|
}
|
|
}
|
|
|
|
func clearVoiceMessageDraft() {
|
|
databaseStorage.asyncWrite { [thread] in VoiceMessageModel.clearDraft(for: thread, transaction: $0) }
|
|
}
|
|
}
|