mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 12:19:54 +00:00
* ios: fix XCode 16 regressions (tap not working on files, quotes, images, voice messages, etc.), open link previews on tap * fix voice recording * fix video, accepting calls from chat, preference toggles in chat * WIP message and meta * handle links in attributed strings * custom attribute for links to prevent race conditions with default tap handler
184 lines
6.5 KiB
Swift
184 lines
6.5 KiB
Swift
//
|
|
// CIMetaView.swift
|
|
// SimpleX
|
|
//
|
|
// Created by Evgeny Poberezkin on 11/02/2022.
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SimpleXChat
|
|
|
|
struct CIMetaView: View {
|
|
@ObservedObject var chat: Chat
|
|
@EnvironmentObject var theme: AppTheme
|
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
|
var chatItem: ChatItem
|
|
var metaColor: Color
|
|
var paleMetaColor = Color(uiColor: .tertiaryLabel)
|
|
var showStatus = true
|
|
var showEdited = true
|
|
var invertedMaterial = false
|
|
|
|
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
|
|
|
|
var body: some View {
|
|
if chatItem.isDeletedContent {
|
|
chatItem.timestampText.font(.caption).foregroundColor(metaColor)
|
|
} else {
|
|
ZStack {
|
|
ciMetaText(
|
|
chatItem.meta,
|
|
chatTTL: chat.chatInfo.timedMessagesTTL,
|
|
encrypted: chatItem.encryptedFile,
|
|
color: metaColor,
|
|
paleColor: paleMetaColor,
|
|
colorMode: invertedMaterial
|
|
? .invertedMaterial
|
|
: .normal,
|
|
showStatus: showStatus,
|
|
showEdited: showEdited,
|
|
showViaProxy: showSentViaProxy,
|
|
showTimesamp: showTimestamp
|
|
).invertedForegroundStyle(enabled: invertedMaterial)
|
|
if invertedMaterial {
|
|
ciMetaText(
|
|
chatItem.meta,
|
|
chatTTL: chat.chatInfo.timedMessagesTTL,
|
|
encrypted: chatItem.encryptedFile,
|
|
colorMode: .normal,
|
|
onlyOverrides: true,
|
|
showStatus: showStatus,
|
|
showEdited: showEdited,
|
|
showViaProxy: showSentViaProxy,
|
|
showTimesamp: showTimestamp
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enum MetaColorMode {
|
|
// Renders provided colours
|
|
case normal
|
|
// Fully transparent meta - used for reserving space
|
|
case transparent
|
|
// Renders white on dark backgrounds and black on light ones
|
|
case invertedMaterial
|
|
|
|
func resolve(_ c: Color?) -> Color? {
|
|
switch self {
|
|
case .normal: c
|
|
case .transparent: .clear
|
|
case .invertedMaterial: nil
|
|
}
|
|
}
|
|
|
|
func statusSpacer(_ sent: Bool) -> Text {
|
|
switch self {
|
|
case .normal, .transparent:
|
|
Text(
|
|
sent
|
|
? Image("checkmark.wide")
|
|
: Image(systemName: "circlebadge.fill")
|
|
).foregroundColor(.clear)
|
|
case .invertedMaterial: textSpace.kerning(13)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ciMetaText(
|
|
_ meta: CIMeta,
|
|
chatTTL: Int?,
|
|
encrypted: Bool?,
|
|
color: Color = .clear, // we use this function to reserve space without rendering meta
|
|
paleColor: Color? = nil,
|
|
primaryColor: Color = .accentColor,
|
|
colorMode: MetaColorMode = .normal,
|
|
onlyOverrides: Bool = false, // only render colors that differ from base
|
|
showStatus: Bool = true,
|
|
showEdited: Bool = true,
|
|
showViaProxy: Bool,
|
|
showTimesamp: Bool
|
|
) -> Text {
|
|
var r = Text("")
|
|
var space: Text? = nil
|
|
let appendSpace = {
|
|
if let sp = space {
|
|
r = r + sp
|
|
space = nil
|
|
}
|
|
}
|
|
let resolved = colorMode.resolve(color)
|
|
if showEdited, meta.itemEdited {
|
|
r = r + statusIconText("pencil", resolved)
|
|
}
|
|
if meta.disappearing {
|
|
r = r + statusIconText("timer", resolved).font(.caption2)
|
|
let ttl = meta.itemTimed?.ttl
|
|
if ttl != chatTTL {
|
|
r = r + colored(Text(shortTimeText(ttl)), resolved)
|
|
}
|
|
space = textSpace
|
|
}
|
|
if showViaProxy, meta.sentViaProxy == true {
|
|
appendSpace()
|
|
r = r + statusIconText("arrow.forward", resolved?.opacity(0.67)).font(.caption2)
|
|
}
|
|
if showStatus {
|
|
appendSpace()
|
|
if let (image, statusColor) = meta.itemStatus.statusIcon(color, paleColor ?? color, primaryColor) {
|
|
let metaColor = if onlyOverrides && statusColor == color {
|
|
Color.clear
|
|
} else {
|
|
colorMode.resolve(statusColor)
|
|
}
|
|
r = r + colored(Text(image), metaColor)
|
|
} else if !meta.disappearing {
|
|
r = r + colorMode.statusSpacer(meta.itemStatus.sent)
|
|
}
|
|
space = textSpace
|
|
}
|
|
if let enc = encrypted {
|
|
appendSpace()
|
|
r = r + statusIconText(enc ? "lock" : "lock.open", resolved)
|
|
space = textSpace
|
|
}
|
|
if showTimesamp {
|
|
appendSpace()
|
|
r = r + colored(meta.timestampText, resolved)
|
|
}
|
|
return r.font(.caption)
|
|
}
|
|
|
|
@inline(__always)
|
|
private func statusIconText(_ icon: String, _ color: Color?) -> Text {
|
|
colored(Text(Image(systemName: icon)), color)
|
|
}
|
|
|
|
// Applying `foregroundColor(nil)` breaks `.invertedForegroundStyle` modifier
|
|
@inline(__always)
|
|
private func colored(_ t: Text, _ color: Color?) -> Text {
|
|
if let color {
|
|
t.foregroundColor(color)
|
|
} else {
|
|
t
|
|
}
|
|
}
|
|
|
|
struct CIMetaView_Previews: PreviewProvider {
|
|
static let metaColor = Color.secondary
|
|
static var previews: some View {
|
|
Group {
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true), metaColor: metaColor)
|
|
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), metaColor: metaColor)
|
|
}
|
|
.previewLayout(.fixed(width: 360, height: 100))
|
|
}
|
|
}
|