2022-03-17 09:42:59 +00:00
|
|
|
//
|
|
|
|
// ComposeView.swift
|
|
|
|
// SimpleX
|
|
|
|
//
|
|
|
|
// Created by Evgeny on 13/03/2022.
|
|
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
2022-05-31 07:55:13 +01:00
|
|
|
import SimpleXChat
|
2022-03-17 09:42:59 +00:00
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
enum ComposePreview {
|
|
|
|
case noPreview
|
2022-05-30 08:59:04 +01:00
|
|
|
case linkPreview(linkPreview: LinkPreview?)
|
2022-04-25 12:44:24 +04:00
|
|
|
case imagePreview(imagePreview: String)
|
2022-05-06 21:10:32 +04:00
|
|
|
case filePreview(fileName: String)
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
2022-04-19 12:29:03 +04:00
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
enum ComposeContextItem {
|
|
|
|
case noContextItem
|
|
|
|
case quotedItem(chatItem: ChatItem)
|
|
|
|
case editingItem(chatItem: ChatItem)
|
|
|
|
}
|
2022-04-08 18:17:10 +01:00
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
struct ComposeState {
|
|
|
|
var message: String
|
|
|
|
var preview: ComposePreview
|
|
|
|
var contextItem: ComposeContextItem
|
2022-03-17 09:42:59 +00:00
|
|
|
var inProgress: Bool = false
|
2022-05-30 08:59:04 +01:00
|
|
|
var useLinkPreviews: Bool = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
|
2022-04-25 12:44:24 +04:00
|
|
|
|
|
|
|
init(
|
|
|
|
message: String = "",
|
|
|
|
preview: ComposePreview = .noPreview,
|
|
|
|
contextItem: ComposeContextItem = .noContextItem
|
|
|
|
) {
|
|
|
|
self.message = message
|
|
|
|
self.preview = preview
|
|
|
|
self.contextItem = contextItem
|
|
|
|
}
|
|
|
|
|
|
|
|
init(editingItem: ChatItem) {
|
|
|
|
self.message = editingItem.content.text
|
|
|
|
self.preview = chatItemPreview(chatItem: editingItem)
|
|
|
|
self.contextItem = .editingItem(chatItem: editingItem)
|
|
|
|
}
|
|
|
|
|
|
|
|
func copy(
|
|
|
|
message: String? = nil,
|
|
|
|
preview: ComposePreview? = nil,
|
|
|
|
contextItem: ComposeContextItem? = nil
|
|
|
|
) -> ComposeState {
|
|
|
|
ComposeState(
|
|
|
|
message: message ?? self.message,
|
|
|
|
preview: preview ?? self.preview,
|
|
|
|
contextItem: contextItem ?? self.contextItem
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func editing() -> Bool {
|
|
|
|
switch contextItem {
|
|
|
|
case .editingItem: return true
|
|
|
|
default: return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendEnabled() -> Bool {
|
|
|
|
switch preview {
|
|
|
|
case .imagePreview:
|
|
|
|
return true
|
2022-05-06 21:10:32 +04:00
|
|
|
case .filePreview:
|
|
|
|
return true
|
2022-04-25 12:44:24 +04:00
|
|
|
default:
|
|
|
|
return !message.isEmpty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func linkPreviewAllowed() -> Bool {
|
|
|
|
switch preview {
|
|
|
|
case .imagePreview:
|
|
|
|
return false
|
2022-05-06 21:10:32 +04:00
|
|
|
case .filePreview:
|
|
|
|
return false
|
2022-04-25 12:44:24 +04:00
|
|
|
default:
|
2022-05-30 08:59:04 +01:00
|
|
|
return useLinkPreviews
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func linkPreview() -> LinkPreview? {
|
|
|
|
switch preview {
|
|
|
|
case let .linkPreview(linkPreview):
|
|
|
|
return linkPreview
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func chatItemPreview(chatItem: ChatItem) -> ComposePreview {
|
|
|
|
let chatItemPreview: ComposePreview
|
|
|
|
switch chatItem.content.msgContent {
|
2022-05-06 21:10:32 +04:00
|
|
|
case .text:
|
|
|
|
chatItemPreview = .noPreview
|
2022-04-25 12:44:24 +04:00
|
|
|
case let .link(_, preview: preview):
|
|
|
|
chatItemPreview = .linkPreview(linkPreview: preview)
|
|
|
|
case let .image(_, image: image):
|
|
|
|
chatItemPreview = .imagePreview(imagePreview: image)
|
2022-05-06 21:10:32 +04:00
|
|
|
case .file:
|
|
|
|
chatItemPreview = .filePreview(fileName: chatItem.file?.fileName ?? "")
|
2022-04-25 12:44:24 +04:00
|
|
|
default:
|
|
|
|
chatItemPreview = .noPreview
|
|
|
|
}
|
|
|
|
return chatItemPreview
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ComposeView: View {
|
|
|
|
@EnvironmentObject var chatModel: ChatModel
|
|
|
|
let chat: Chat
|
|
|
|
@Binding var composeState: ComposeState
|
2022-03-17 09:42:59 +00:00
|
|
|
@FocusState.Binding var keyboardVisible: Bool
|
2022-04-25 12:44:24 +04:00
|
|
|
|
2022-04-08 18:17:10 +01:00
|
|
|
@State var linkUrl: URL? = nil
|
|
|
|
@State var prevLinkUrl: URL? = nil
|
|
|
|
@State var pendingLinkUrl: URL? = nil
|
|
|
|
@State var cancelledLinks: Set<String> = []
|
|
|
|
|
2022-04-19 12:29:03 +04:00
|
|
|
@State private var showChooseSource = false
|
|
|
|
@State private var showImagePicker = false
|
2022-05-18 21:32:30 +04:00
|
|
|
@State private var showTakePhoto = false
|
2022-05-06 21:10:32 +04:00
|
|
|
@State var chosenImage: UIImage? = nil
|
|
|
|
@State private var showFileImporter = false
|
|
|
|
@State var chosenFile: URL? = nil
|
2022-04-08 18:17:10 +01:00
|
|
|
|
2022-03-17 09:42:59 +00:00
|
|
|
var body: some View {
|
|
|
|
VStack(spacing: 0) {
|
2022-04-25 12:44:24 +04:00
|
|
|
contextItemView()
|
2022-05-06 21:10:32 +04:00
|
|
|
switch (composeState.editing(), composeState.preview) {
|
|
|
|
case (true, .filePreview): EmptyView()
|
|
|
|
default: previewView()
|
|
|
|
}
|
2022-04-25 12:44:24 +04:00
|
|
|
HStack (alignment: .bottom) {
|
|
|
|
Button {
|
|
|
|
showChooseSource = true
|
|
|
|
} label: {
|
|
|
|
Image(systemName: "paperclip")
|
|
|
|
.resizable()
|
|
|
|
}
|
|
|
|
.disabled(composeState.editing())
|
|
|
|
.frame(width: 25, height: 25)
|
|
|
|
.padding(.bottom, 12)
|
|
|
|
.padding(.leading, 12)
|
2022-04-19 12:29:03 +04:00
|
|
|
SendMessageView(
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState: $composeState,
|
2022-04-27 20:54:21 +04:00
|
|
|
sendMessage: {
|
|
|
|
sendMessage()
|
2022-04-19 12:29:03 +04:00
|
|
|
resetLinkPreview()
|
|
|
|
},
|
2022-04-25 12:44:24 +04:00
|
|
|
keyboardVisible: $keyboardVisible
|
2022-04-19 12:29:03 +04:00
|
|
|
)
|
2022-04-25 12:44:24 +04:00
|
|
|
.padding(.trailing, 12)
|
2022-04-19 12:29:03 +04:00
|
|
|
.background(.background)
|
|
|
|
}
|
2022-03-17 09:42:59 +00:00
|
|
|
}
|
2022-04-25 12:44:24 +04:00
|
|
|
.onChange(of: composeState.message) { _ in
|
|
|
|
if composeState.linkPreviewAllowed() {
|
|
|
|
if composeState.message.count > 0 {
|
|
|
|
showLinkPreview(composeState.message)
|
|
|
|
} else {
|
|
|
|
resetLinkPreview()
|
|
|
|
}
|
2022-04-08 18:17:10 +01:00
|
|
|
}
|
2022-03-25 22:26:05 +04:00
|
|
|
}
|
2022-04-19 12:29:03 +04:00
|
|
|
.confirmationDialog("Attach", isPresented: $showChooseSource, titleVisibility: .visible) {
|
|
|
|
Button("Take picture") {
|
2022-05-18 21:32:30 +04:00
|
|
|
showTakePhoto = true
|
2022-04-19 12:29:03 +04:00
|
|
|
}
|
|
|
|
Button("Choose from library") {
|
|
|
|
showImagePicker = true
|
|
|
|
}
|
2022-05-30 09:03:56 +01:00
|
|
|
if UIPasteboard.general.hasImages {
|
|
|
|
Button("Paste image") {
|
|
|
|
chosenImage = UIPasteboard.general.image
|
|
|
|
}
|
|
|
|
}
|
2022-05-06 21:10:32 +04:00
|
|
|
Button("Choose file") {
|
|
|
|
showFileImporter = true
|
|
|
|
}
|
2022-04-19 12:29:03 +04:00
|
|
|
}
|
2022-05-18 21:32:30 +04:00
|
|
|
.fullScreenCover(isPresented: $showTakePhoto) {
|
|
|
|
ZStack {
|
|
|
|
Color.black.edgesIgnoringSafeArea(.all)
|
2022-04-19 12:29:03 +04:00
|
|
|
CameraImagePicker(image: $chosenImage)
|
|
|
|
}
|
|
|
|
}
|
2022-05-18 21:32:30 +04:00
|
|
|
.sheet(isPresented: $showImagePicker) {
|
|
|
|
LibraryImagePicker(image: $chosenImage) {
|
|
|
|
didSelectItem in showImagePicker = false
|
|
|
|
}
|
|
|
|
}
|
2022-04-19 12:29:03 +04:00
|
|
|
.onChange(of: chosenImage) { image in
|
2022-04-25 12:44:24 +04:00
|
|
|
if let image = image,
|
|
|
|
let imagePreview = resizeImageToStrSize(image, maxDataSize: 14000) {
|
|
|
|
composeState = composeState.copy(preview: .imagePreview(imagePreview: imagePreview))
|
2022-04-19 12:29:03 +04:00
|
|
|
} else {
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState = composeState.copy(preview: .noPreview)
|
|
|
|
}
|
|
|
|
}
|
2022-05-06 21:10:32 +04:00
|
|
|
.fileImporter(
|
|
|
|
isPresented: $showFileImporter,
|
|
|
|
allowedContentTypes: [.data],
|
|
|
|
allowsMultipleSelection: false
|
|
|
|
) { result in
|
2022-06-24 13:52:20 +01:00
|
|
|
if case let .success(files) = result, let fileURL = files.first {
|
2022-05-06 21:10:32 +04:00
|
|
|
do {
|
|
|
|
var fileSize: Int? = nil
|
|
|
|
if fileURL.startAccessingSecurityScopedResource() {
|
|
|
|
let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
|
|
|
|
fileSize = resourceValues.fileSize
|
|
|
|
}
|
|
|
|
fileURL.stopAccessingSecurityScopedResource()
|
|
|
|
if let fileSize = fileSize,
|
|
|
|
fileSize <= maxFileSize {
|
|
|
|
chosenFile = fileURL
|
|
|
|
composeState = composeState.copy(preview: .filePreview(fileName: fileURL.lastPathComponent))
|
|
|
|
} else {
|
|
|
|
let prettyMaxFileSize = ByteCountFormatter().string(fromByteCount: maxFileSize)
|
|
|
|
AlertManager.shared.showAlertMsg(
|
|
|
|
title: "Large file!",
|
|
|
|
message: "Currently maximum supported file size is \(prettyMaxFileSize)."
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
logger.error("ComposeView fileImporter error \(error.localizedDescription)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder func previewView() -> some View {
|
|
|
|
switch composeState.preview {
|
|
|
|
case .noPreview:
|
|
|
|
EmptyView()
|
|
|
|
case let .linkPreview(linkPreview: preview):
|
|
|
|
ComposeLinkView(linkPreview: preview, cancelPreview: cancelLinkPreview)
|
|
|
|
case let .imagePreview(imagePreview: img):
|
|
|
|
ComposeImageView(
|
|
|
|
image: img,
|
2022-05-06 21:10:32 +04:00
|
|
|
cancelImage: {
|
|
|
|
composeState = composeState.copy(preview: .noPreview)
|
|
|
|
chosenImage = nil
|
|
|
|
},
|
|
|
|
cancelEnabled: !composeState.editing())
|
|
|
|
case let .filePreview(fileName: fileName):
|
|
|
|
ComposeFileView(
|
|
|
|
fileName: fileName,
|
|
|
|
cancelFile: {
|
|
|
|
composeState = composeState.copy(preview: .noPreview)
|
|
|
|
chosenFile = nil
|
|
|
|
},
|
2022-04-25 12:44:24 +04:00
|
|
|
cancelEnabled: !composeState.editing())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder private func contextItemView() -> some View {
|
|
|
|
switch composeState.contextItem {
|
|
|
|
case .noContextItem:
|
|
|
|
EmptyView()
|
|
|
|
case let .quotedItem(chatItem: quotedItem):
|
2022-05-06 21:10:32 +04:00
|
|
|
ContextItemView(
|
|
|
|
contextItem: quotedItem,
|
2022-05-09 09:12:32 +01:00
|
|
|
contextIcon: "arrowshape.turn.up.left",
|
2022-05-06 21:10:32 +04:00
|
|
|
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) }
|
|
|
|
)
|
2022-04-25 12:44:24 +04:00
|
|
|
case let .editingItem(chatItem: editingItem):
|
2022-05-06 21:10:32 +04:00
|
|
|
ContextItemView(
|
|
|
|
contextItem: editingItem,
|
2022-05-09 09:12:32 +01:00
|
|
|
contextIcon: "pencil",
|
2022-05-06 21:10:32 +04:00
|
|
|
cancelContextItem: { clearState() }
|
|
|
|
)
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-27 20:54:21 +04:00
|
|
|
private func sendMessage() {
|
2022-04-25 12:44:24 +04:00
|
|
|
logger.debug("ChatView sendMessage")
|
|
|
|
Task {
|
|
|
|
logger.debug("ChatView sendMessage: in Task")
|
2022-09-21 17:18:48 +04:00
|
|
|
switch composeState.contextItem {
|
|
|
|
case let .editingItem(chatItem: ei):
|
|
|
|
if let oldMsgContent = ei.content.msgContent {
|
|
|
|
do {
|
2022-09-24 22:20:56 +01:00
|
|
|
let mc = updateMsgContent(oldMsgContent)
|
|
|
|
await MainActor.run { clearState() }
|
2022-04-25 12:44:24 +04:00
|
|
|
let chatItem = try await apiUpdateChatItem(
|
|
|
|
type: chat.chatInfo.chatType,
|
|
|
|
id: chat.chatInfo.apiId,
|
|
|
|
itemId: ei.id,
|
2022-09-24 22:20:56 +01:00
|
|
|
msg: mc
|
2022-04-25 12:44:24 +04:00
|
|
|
)
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
let _ = self.chatModel.upsertChatItem(self.chat.chatInfo, chatItem)
|
|
|
|
}
|
2022-09-21 17:18:48 +04:00
|
|
|
} catch {
|
|
|
|
logger.error("ChatView.sendMessage error: \(error.localizedDescription)")
|
|
|
|
AlertManager.shared.showAlertMsg(title: "Error updating message", message: "Error: \(responseError(error))")
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
2022-09-24 22:20:56 +01:00
|
|
|
} else {
|
|
|
|
await MainActor.run { clearState() }
|
2022-09-21 17:18:48 +04:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
var mc: MsgContent? = nil
|
|
|
|
var file: String? = nil
|
|
|
|
switch (composeState.preview) {
|
|
|
|
case .noPreview:
|
|
|
|
mc = .text(composeState.message)
|
|
|
|
case .linkPreview:
|
|
|
|
mc = checkLinkPreview()
|
|
|
|
case let .imagePreview(imagePreview: image):
|
|
|
|
if let uiImage = chosenImage,
|
|
|
|
let savedFile = saveImage(uiImage) {
|
|
|
|
mc = .image(text: composeState.message, image: image)
|
|
|
|
file = savedFile
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
2022-09-21 17:18:48 +04:00
|
|
|
case .filePreview:
|
|
|
|
if let fileURL = chosenFile,
|
|
|
|
let savedFile = saveFileFromURL(fileURL) {
|
|
|
|
mc = .file(composeState.message)
|
|
|
|
file = savedFile
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
|
|
|
}
|
2022-09-21 17:18:48 +04:00
|
|
|
|
|
|
|
var quotedItemId: Int64? = nil
|
|
|
|
switch (composeState.contextItem) {
|
|
|
|
case let .quotedItem(chatItem: quotedItem):
|
|
|
|
quotedItemId = quotedItem.id
|
|
|
|
default:
|
|
|
|
quotedItemId = nil
|
|
|
|
}
|
2022-09-24 22:20:56 +01:00
|
|
|
await MainActor.run { clearState() }
|
2022-09-21 17:18:48 +04:00
|
|
|
if let mc = mc,
|
|
|
|
let chatItem = await apiSendMessage(
|
|
|
|
type: chat.chatInfo.chatType,
|
|
|
|
id: chat.chatInfo.apiId,
|
|
|
|
file: file,
|
|
|
|
quotedItemId: quotedItemId,
|
|
|
|
msg: mc
|
|
|
|
) {
|
|
|
|
chatModel.addChatItem(chat.chatInfo, chatItem)
|
|
|
|
}
|
2022-04-19 12:29:03 +04:00
|
|
|
}
|
|
|
|
}
|
2022-04-25 12:44:24 +04:00
|
|
|
}
|
|
|
|
|
2022-05-06 21:10:32 +04:00
|
|
|
private func clearState() {
|
|
|
|
composeState = ComposeState()
|
|
|
|
linkUrl = nil
|
|
|
|
prevLinkUrl = nil
|
|
|
|
pendingLinkUrl = nil
|
|
|
|
cancelledLinks = []
|
|
|
|
chosenImage = nil
|
|
|
|
chosenFile = nil
|
|
|
|
}
|
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
private func updateMsgContent(_ msgContent: MsgContent) -> MsgContent {
|
|
|
|
switch msgContent {
|
|
|
|
case .text:
|
|
|
|
return checkLinkPreview()
|
|
|
|
case .link:
|
|
|
|
return checkLinkPreview()
|
|
|
|
case .image(_, let image):
|
|
|
|
return .image(text: composeState.message, image: image)
|
2022-05-04 09:10:36 +04:00
|
|
|
case .file:
|
|
|
|
return .file(composeState.message)
|
2022-04-25 12:44:24 +04:00
|
|
|
case .unknown(let type, _):
|
|
|
|
return .unknown(type: type, text: composeState.message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 09:39:04 +01:00
|
|
|
private func showLinkPreview(_ s: String) {
|
|
|
|
prevLinkUrl = linkUrl
|
|
|
|
linkUrl = parseMessage(s)
|
|
|
|
if let url = linkUrl {
|
2022-04-25 12:44:24 +04:00
|
|
|
if url != composeState.linkPreview()?.uri && url != pendingLinkUrl {
|
2022-04-11 09:39:04 +01:00
|
|
|
pendingLinkUrl = url
|
|
|
|
if prevLinkUrl == url {
|
|
|
|
loadLinkPreview(url)
|
|
|
|
} else {
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
|
|
|
loadLinkPreview(url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState = composeState.copy(preview: .noPreview)
|
2022-04-11 09:39:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func parseMessage(_ msg: String) -> URL? {
|
2022-06-24 13:52:20 +01:00
|
|
|
let parsedMsg = parseSimpleXMarkdown(msg)
|
|
|
|
let uri = parsedMsg?.first(where: { ft in
|
|
|
|
ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text)
|
|
|
|
})
|
|
|
|
if let uri = uri { return URL(string: uri.text) }
|
|
|
|
else { return nil }
|
2022-04-11 09:39:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private func isSimplexLink(_ link: String) -> Bool {
|
|
|
|
link.starts(with: "https://simplex.chat") || link.starts(with: "http://simplex.chat")
|
|
|
|
}
|
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
private func cancelLinkPreview() {
|
|
|
|
if let uri = composeState.linkPreview()?.uri.absoluteString {
|
2022-04-11 09:39:04 +01:00
|
|
|
cancelledLinks.insert(uri)
|
|
|
|
}
|
2022-05-30 08:59:04 +01:00
|
|
|
pendingLinkUrl = nil
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState = composeState.copy(preview: .noPreview)
|
2022-04-11 09:39:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private func loadLinkPreview(_ url: URL) {
|
|
|
|
if pendingLinkUrl == url {
|
2022-05-30 08:59:04 +01:00
|
|
|
composeState = composeState.copy(preview: .linkPreview(linkPreview: nil))
|
2022-04-25 12:44:24 +04:00
|
|
|
getLinkPreview(url: url) { linkPreview in
|
|
|
|
if let linkPreview = linkPreview,
|
|
|
|
pendingLinkUrl == url {
|
|
|
|
composeState = composeState.copy(preview: .linkPreview(linkPreview: linkPreview))
|
2022-04-08 18:17:10 +01:00
|
|
|
pendingLinkUrl = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 09:39:04 +01:00
|
|
|
private func resetLinkPreview() {
|
2022-04-08 18:17:10 +01:00
|
|
|
linkUrl = nil
|
|
|
|
prevLinkUrl = nil
|
|
|
|
pendingLinkUrl = nil
|
|
|
|
cancelledLinks = []
|
|
|
|
}
|
2022-04-25 12:44:24 +04:00
|
|
|
|
|
|
|
private func checkLinkPreview() -> MsgContent {
|
|
|
|
switch (composeState.preview) {
|
|
|
|
case let .linkPreview(linkPreview: linkPreview):
|
|
|
|
if let url = parseMessage(composeState.message),
|
2022-05-30 08:59:04 +01:00
|
|
|
let linkPreview = linkPreview,
|
2022-04-25 12:44:24 +04:00
|
|
|
url == linkPreview.uri {
|
|
|
|
return .link(text: composeState.message, preview: linkPreview)
|
|
|
|
} else {
|
|
|
|
return .text(composeState.message)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return .text(composeState.message)
|
|
|
|
}
|
|
|
|
}
|
2022-03-17 09:42:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ComposeView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
2022-04-25 12:44:24 +04:00
|
|
|
let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
|
|
|
@State var composeState = ComposeState(message: "hello")
|
2022-03-17 09:42:59 +00:00
|
|
|
@FocusState var keyboardVisible: Bool
|
|
|
|
|
2022-03-25 22:26:05 +04:00
|
|
|
return Group {
|
|
|
|
ComposeView(
|
2022-04-25 12:44:24 +04:00
|
|
|
chat: chat,
|
|
|
|
composeState: $composeState,
|
|
|
|
keyboardVisible: $keyboardVisible
|
2022-03-25 22:26:05 +04:00
|
|
|
)
|
|
|
|
ComposeView(
|
2022-04-25 12:44:24 +04:00
|
|
|
chat: chat,
|
|
|
|
composeState: $composeState,
|
|
|
|
keyboardVisible: $keyboardVisible
|
2022-03-25 22:26:05 +04:00
|
|
|
)
|
|
|
|
}
|
2022-03-17 09:42:59 +00:00
|
|
|
}
|
|
|
|
}
|