Skip to content

Commit

Permalink
ios: update chat previews, show filename in drafts (#1847)
Browse files Browse the repository at this point in the history
* ios: update chat previews, show filename in drafts

* save and restore images/file/voice for draft

* refactor image

* it was a wrong value

* use param label

* proper stop of voice recording

* safe draft logic

* different way of finishing recording

* keep condition

* refactor

* fix live

* fix

* refactor

* fix

* simplify

* add space after filename in draft

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
  • Loading branch information
epoberezkin and avently committed Jan 27, 2023
1 parent 6e131e0 commit 3e56027
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 44 deletions.
4 changes: 4 additions & 0 deletions apps/ios/Shared/AppDelegate.swift
Expand Up @@ -77,6 +77,10 @@ class AppDelegate: NSObject, UIApplicationDelegate {

func applicationWillTerminate(_ application: UIApplication) {
logger.debug("AppDelegate: applicationWillTerminate")
ChatModel.shared.filesToDelete.forEach {
removeFile($0)
}
ChatModel.shared.filesToDelete = []
terminateChat()
}

Expand Down
2 changes: 2 additions & 0 deletions apps/ios/Shared/Model/ChatModel.swift
Expand Up @@ -63,6 +63,8 @@ final class ChatModel: ObservableObject {

var messageDelivery: Dictionary<Int64, () -> Void> = [:]

var filesToDelete: [String] = []

static let shared = ChatModel()

static var ok: Bool { ChatModel.shared.chatDbStatus == .ok }
Expand Down
6 changes: 5 additions & 1 deletion apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift
Expand Up @@ -84,7 +84,7 @@ struct MsgContentView: View {
}
}

func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: String?, preview: Bool = false) -> Text {
func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: String?, icon: String? = nil, preview: Bool = false) -> Text {
let s = text
var res: Text
if let ft = formattedText, ft.count > 0 {
Expand All @@ -98,6 +98,10 @@ func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: St
res = Text(s)
}

if let i = icon {
res = Text(Image(systemName: i)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ") + res
}

if let s = sender {
let t = Text(s)
return (preview ? t : t.fontWeight(.medium)) + Text(": ") + res
Expand Down
6 changes: 1 addition & 5 deletions apps/ios/Shared/Views/Chat/ChatView.swift
Expand Up @@ -52,7 +52,7 @@ struct ChatView: View {
}

Spacer(minLength: 0)

ComposeView(
chat: chat,
composeState: $composeState,
Expand All @@ -79,10 +79,6 @@ struct ChatView: View {
.onDisappear {
if chatModel.chatId == cInfo.id {
chatModel.chatId = nil
if !composeState.empty {
chatModel.draft = composeState
chatModel.draftChatId = chat.id
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
if chatModel.chatId == nil {
chatModel.reversedChatItems = []
Expand Down
101 changes: 70 additions & 31 deletions apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift
Expand Up @@ -14,9 +14,9 @@ import PhotosUI
enum ComposePreview {
case noPreview
case linkPreview(linkPreview: LinkPreview?)
case imagePreviews(imagePreviews: [String])
case imagePreviews(imagePreviews: [(String, UploadContent?)])
case voicePreview(recordingFileName: String, duration: Int)
case filePreview(fileName: String)
case filePreview(fileName: String, file: URL)
}

enum ComposeContextItem {
Expand Down Expand Up @@ -163,7 +163,7 @@ struct ComposeState {
}

var empty: Bool {
message == "" && liveMessage == nil && noPreview
message == "" && noPreview
}
}

Expand All @@ -175,11 +175,12 @@ func chatItemPreview(chatItem: ChatItem) -> ComposePreview {
case let .link(_, preview: preview):
chatItemPreview = .linkPreview(linkPreview: preview)
case let .image(_, image):
chatItemPreview = .imagePreviews(imagePreviews: [image])
chatItemPreview = .imagePreviews(imagePreviews: [(image, nil)])
case let .voice(_, duration):
chatItemPreview = .voicePreview(recordingFileName: chatItem.file?.fileName ?? "", duration: duration)
case .file:
chatItemPreview = .filePreview(fileName: chatItem.file?.fileName ?? "")
let fileName = chatItem.file?.fileName ?? ""
chatItemPreview = .filePreview(fileName: fileName, file: getAppFilePath(fileName))
default:
chatItemPreview = .noPreview
}
Expand Down Expand Up @@ -233,7 +234,6 @@ struct ComposeView: View {
@State private var showTakePhoto = false
@State var chosenImages: [UploadContent] = []
@State private var showFileImporter = false
@State var chosenFile: URL? = nil

@State private var audioRecorder: AudioRecorder?
@State private var voiceMessageRecordingTime: TimeInterval?
Expand Down Expand Up @@ -341,10 +341,10 @@ struct ComposeView: View {
}
.onChange(of: chosenImages) { images in
Task {
var imgs: [String] = []
var imgs: [(String, UploadContent)] = []
for image in images {
if let img = resizeImageToStrSize(image.uiImage, maxDataSize: 14000) {
imgs.append(img)
imgs.append((img, image))
await MainActor.run {
composeState = composeState.copy(preview: .imagePreviews(imagePreviews: imgs))
}
Expand All @@ -371,9 +371,8 @@ struct ComposeView: View {
}
fileURL.stopAccessingSecurityScopedResource()
if let fileSize = fileSize,
fileSize <= MAX_FILE_SIZE {
chosenFile = fileURL
composeState = composeState.copy(preview: .filePreview(fileName: fileURL.lastPathComponent))
fileSize <= MAX_FILE_SIZE {
composeState = composeState.copy(preview: .filePreview(fileName: fileURL.lastPathComponent, file: fileURL))
} else {
let prettyMaxFileSize = ByteCountFormatter().string(fromByteCount: MAX_FILE_SIZE)
AlertManager.shared.showAlertMsg(
Expand All @@ -387,12 +386,18 @@ struct ComposeView: View {
}
}
.onDisappear {
if let fileName = composeState.voiceMessageRecordingFileName {
cancelVoiceMessageRecording(fileName)
}
if composeState.liveMessage != nil && (!composeState.message.isEmpty || composeState.liveMessage?.sentMsg != nil) {
if composeState.liveMessage != nil
&& (!composeState.message.isEmpty || composeState.liveMessage?.sentMsg != nil) {
cancelCurrentVoiceRecording()
clearCurrentDraft()
sendMessage()
resetLinkPreview()
} else if !composeState.empty {
saveCurrentDraft()
} else {
cancelCurrentVoiceRecording()
clearCurrentDraft()
clearState()
}
chatModel.removeLiveDummy(animated: false)
}
Expand All @@ -409,6 +414,12 @@ struct ComposeView: View {
if !vmAllowed && composeState.voicePreview,
let fileName = composeState.voiceMessageRecordingFileName {
cancelVoiceMessageRecording(fileName)
clearState()
}
}
.onAppear {
if case let .voicePreview(_, duration) = composeState.preview {
voiceMessageRecordingTime = TimeInterval(duration)
}
}
}
Expand Down Expand Up @@ -475,7 +486,7 @@ struct ComposeView: View {
ComposeLinkView(linkPreview: preview, cancelPreview: cancelLinkPreview)
case let .imagePreviews(imagePreviews: images):
ComposeImageView(
images: images,
images: images.map { (img, _) in img },
cancelImage: {
composeState = composeState.copy(preview: .noPreview)
chosenImages = []
Expand All @@ -486,16 +497,18 @@ struct ComposeView: View {
recordingFileName: recordingFileName,
recordingTime: $voiceMessageRecordingTime,
recordingState: $composeState.voiceMessageRecordingState,
cancelVoiceMessage: { cancelVoiceMessageRecording($0) },
cancelVoiceMessage: {
cancelVoiceMessageRecording($0)
clearState()
},
cancelEnabled: !composeState.editing,
stopPlayback: $stopPlayback
)
case let .filePreview(fileName: fileName):
case let .filePreview(fileName, _):
ComposeFileView(
fileName: fileName,
cancelFile: {
composeState = composeState.copy(preview: .noPreview)
chosenFile = nil
},
cancelEnabled: !composeState.editing)
}
Expand Down Expand Up @@ -552,27 +565,23 @@ struct ComposeView: View {
case .linkPreview:
sent = await send(checkLinkPreview(), quoted: quoted, live: live)
case let .imagePreviews(imagePreviews: images):
let last = min(chosenImages.count, images.count) - 1
let last = images.count - 1
if last >= 0 {
for i in 0..<last {
if let savedFile = saveAnyImage(chosenImages[i]) {
_ = await send(.image(text: "", image: images[i]), quoted: nil, file: savedFile)
}
sent = await sendImage(images[i])
_ = try? await Task.sleep(nanoseconds: 100_000000)
}
if let savedFile = saveAnyImage(chosenImages[last]) {
sent = await send(.image(text: msgText, image: images[last]), quoted: quoted, file: savedFile, live: live)
}
sent = await sendImage(images[last], text: msgText, quoted: quoted, live: live)
}
if sent == nil {
sent = await send(.text(msgText), quoted: quoted, live: live)
}
case let .voicePreview(recordingFileName, duration):
stopPlayback.toggle()
chatModel.filesToDelete.removeAll { $0 == recordingFileName }
sent = await send(.voice(text: msgText, duration: duration), quoted: quoted, file: recordingFileName)
case .filePreview:
if let fileURL = chosenFile,
let savedFile = saveFileFromURL(fileURL) {
case let .filePreview(_, file):
if let savedFile = saveFileFromURL(file) {
sent = await send(.file(msgText), quoted: quoted, file: savedFile, live: live)
}
}
Expand Down Expand Up @@ -627,6 +636,14 @@ struct ComposeView: View {
}
}

func sendImage(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false) async -> ChatItem? {
let (image, data) = imageData
if let data = data, let savedFile = saveAnyImage(data) {
return await send(.image(text: text, image: image), quoted: quoted, file: savedFile, live: live)
}
return nil
}

func send(_ mc: MsgContent, quoted: Int64?, file: String? = nil, live: Bool = false) async -> ChatItem? {
if let chatItem = await apiSendMessage(
type: chat.chatInfo.chatType,
Expand Down Expand Up @@ -737,11 +754,16 @@ struct ComposeView: View {
)
}

private func cancelCurrentVoiceRecording() {
if let fileName = composeState.voiceMessageRecordingFileName {
cancelVoiceMessageRecording(fileName)
}
}

private func cancelVoiceMessageRecording(_ fileName: String) {
stopPlayback.toggle()
audioRecorder?.stop()
removeFile(fileName)
clearState()
}

private func clearState(live: Bool = false) {
Expand All @@ -753,12 +775,29 @@ struct ComposeView: View {
resetLinkPreview()
}
chosenImages = []
chosenFile = nil
audioRecorder = nil
voiceMessageRecordingTime = nil
startingRecording = false
}

private func saveCurrentDraft() {
if case .recording = composeState.voiceMessageRecordingState {
finishVoiceMessageRecording()
if let fileName = composeState.voiceMessageRecordingFileName {
chatModel.filesToDelete.append(fileName)
}
}
chatModel.draft = composeState
chatModel.draftChatId = chat.id
}

private func clearCurrentDraft() {
if chatModel.draftChatId == chat.id {
chatModel.draft = nil
chatModel.draftChatId = nil
}
}

private func showLinkPreview(_ s: String) {
prevLinkUrl = linkUrl
linkUrl = parseMessage(s)
Expand Down
27 changes: 20 additions & 7 deletions apps/ios/Shared/Views/ChatList/ChatPreviewView.swift
Expand Up @@ -40,7 +40,7 @@ struct ChatPreviewView: View {
.padding(.horizontal, 8)

ZStack(alignment: .topTrailing) {
chatPreviewText(cItem)
chatMessagePreview(cItem)
if case .direct = chat.chatInfo {
chatStatusImage()
.padding(.top, 24)
Expand Down Expand Up @@ -142,21 +142,34 @@ struct ChatPreviewView: View {

func attachment() -> Text {
switch draft.preview {
case .filePreview: return image("doc.fill")
case let .filePreview(fileName, _): return image("doc.fill") + Text(fileName) + Text(" ")
case .imagePreviews: return image("photo")
case let .voicePreview(_, duration): return image("play.fill") + Text(voiceMessageTime(TimeInterval(duration)))
case let .voicePreview(_, duration): return image("play.fill") + Text(durationText(duration))
default: return Text("")
}
}
}

@ViewBuilder private func chatPreviewText(_ cItem: ChatItem?) -> some View {
func chatItemPreview(_ cItem: ChatItem) -> Text {
let itemText = !cItem.meta.itemDeleted ? cItem.text : NSLocalizedString("marked deleted", comment: "marked deleted chat item preview text")
let itemFormattedText = !cItem.meta.itemDeleted ? cItem.formattedText : nil
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true)

func attachment() -> String? {
switch cItem.content.msgContent {
case .file: return "doc.fill"
case .image: return "photo"
case .voice: return "play.fill"
default: return nil
}
}
}

@ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?) -> some View {
if chatModel.draftChatId == chat.id, let draft = chatModel.draft {
chatPreviewLayout(messageDraft(draft))
} else if let cItem = cItem {
let itemText = !cItem.meta.itemDeleted ? cItem.text : NSLocalizedString("marked deleted", comment: "marked deleted chat item preview text")
let itemFormattedText = !cItem.meta.itemDeleted ? cItem.formattedText : nil
chatPreviewLayout(itemStatusMark(cItem) + messageText(itemText, itemFormattedText, cItem.memberDisplayName, preview: true))
chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem))
} else {
switch (chat.chatInfo) {
case let .direct(contact):
Expand Down

0 comments on commit 3e56027

Please sign in to comment.