edit top bar in msg
This commit is contained in:
parent
ab9f5eca71
commit
f47181877e
@ -719,7 +719,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Избранные сообщения" : {
|
"Избранные сообщения" : {
|
||||||
|
"comment" : "Saved messages title"
|
||||||
},
|
},
|
||||||
"Изменение контакта \"%1$@\" появится позже." : {
|
"Изменение контакта \"%1$@\" появится позже." : {
|
||||||
"comment" : "Contacts edit placeholder message"
|
"comment" : "Contacts edit placeholder message"
|
||||||
@ -1420,7 +1420,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Неизвестный пользователь" : {
|
"Неизвестный пользователь" : {
|
||||||
"comment" : "Deleted user display name",
|
"comment" : "Deleted user display name\nUnknown chat title",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -1628,6 +1628,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Оффлайн" : {
|
||||||
|
"comment" : "Offline status placeholder"
|
||||||
|
},
|
||||||
"Оценка %d" : {
|
"Оценка %d" : {
|
||||||
"comment" : "feedback: rating accessibility",
|
"comment" : "feedback: rating accessibility",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ struct PrivateChatView: View {
|
|||||||
let chat: PrivateChatListItem
|
let chat: PrivateChatListItem
|
||||||
let currentUserId: String?
|
let currentUserId: String?
|
||||||
private let bottomAnchorId = "PrivateChatBottomAnchor"
|
private let bottomAnchorId = "PrivateChatBottomAnchor"
|
||||||
|
private let headerAvatarSize: CGFloat = 36
|
||||||
|
|
||||||
let lineLimitInChat = 6
|
let lineLimitInChat = 6
|
||||||
|
|
||||||
@ -46,8 +47,13 @@ struct PrivateChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(title)
|
.navigationTitle(toolbarTitle)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .principal) {
|
||||||
|
chatToolbarContent
|
||||||
|
}
|
||||||
|
}
|
||||||
.task {
|
.task {
|
||||||
viewModel.loadInitialHistory()
|
viewModel.loadInitialHistory()
|
||||||
}
|
}
|
||||||
@ -454,16 +460,188 @@ struct PrivateChatView: View {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
private var title: String {
|
private var title: String {
|
||||||
if let full = chat.chatData?.fullName, !full.isEmpty {
|
switch chat.chatType {
|
||||||
return full
|
case .self:
|
||||||
|
return NSLocalizedString("Избранные сообщения", comment: "Saved messages title")
|
||||||
|
case .privateChat, .unknown:
|
||||||
|
if let custom = trimmed(chat.chatData?.customName) {
|
||||||
|
return custom
|
||||||
|
}
|
||||||
|
if let full = trimmed(chat.chatData?.fullName) {
|
||||||
|
return full
|
||||||
|
}
|
||||||
|
if let login = trimmed(chat.chatData?.login) {
|
||||||
|
return "@\(login)"
|
||||||
|
}
|
||||||
|
return NSLocalizedString("Неизвестный пользователь", comment: "Unknown chat title")
|
||||||
}
|
}
|
||||||
if let custom = chat.chatData?.customName, !custom.isEmpty {
|
}
|
||||||
return custom
|
|
||||||
|
private var toolbarTitle: String {
|
||||||
|
officialDisplayName ?? title
|
||||||
|
}
|
||||||
|
|
||||||
|
private var offlineStatusText: String {
|
||||||
|
NSLocalizedString("Оффлайн", comment: "Offline status placeholder")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var loginDisplay: String? {
|
||||||
|
guard let login = trimmed(chat.chatData?.login) else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if let login = chat.chatData?.login, !login.isEmpty {
|
return "@\(login)"
|
||||||
return "@\(login)"
|
}
|
||||||
|
|
||||||
|
private var isOfficial: Bool {
|
||||||
|
chat.chatData?.isOfficial ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
private var officialDisplayName: String? {
|
||||||
|
guard isOfficial else { return nil }
|
||||||
|
|
||||||
|
if let customName = trimmed(chat.chatData?.customName) {
|
||||||
|
return customName
|
||||||
}
|
}
|
||||||
return NSLocalizedString("Чат", comment: "")
|
|
||||||
|
if let name = trimmed(chat.chatData?.fullName) {
|
||||||
|
return NSLocalizedString(name, comment: "Official chat name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginDisplay
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isDeletedUser: Bool {
|
||||||
|
guard chat.chatType != .self else { return false }
|
||||||
|
return trimmed(chat.chatData?.login) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private var avatarBackgroundColor: Color {
|
||||||
|
if isDeletedUser {
|
||||||
|
return Color(.systemGray5)
|
||||||
|
}
|
||||||
|
return isOfficial ? Color.accentColor.opacity(0.85) : Color.accentColor.opacity(0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var avatarTextColor: Color {
|
||||||
|
if isDeletedUser {
|
||||||
|
return Color.accentColor
|
||||||
|
}
|
||||||
|
return isOfficial ? Color.white : Color.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
private var deletedUserSymbolName: String {
|
||||||
|
"person.slash"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var avatarUrl: URL? {
|
||||||
|
guard let chatData = chat.chatData,
|
||||||
|
let fileId = chatData.avatars?.current?.fileId else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let userId = chatData.userId
|
||||||
|
return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(userId)?file_id=\(fileId)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var avatarInitial: String {
|
||||||
|
if let name = trimmed(chat.chatData?.customName) ?? trimmed(chat.chatData?.fullName) {
|
||||||
|
let components = name.split(separator: " ")
|
||||||
|
let initials = components.prefix(2).compactMap { $0.first }
|
||||||
|
if !initials.isEmpty {
|
||||||
|
return initials.map { String($0) }.joined().uppercased()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let login = trimmed(chat.chatData?.login) {
|
||||||
|
return String(login.prefix(1)).uppercased()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func trimmed(_ string: String?) -> String? {
|
||||||
|
guard let string = string?.trimmingCharacters(in: .whitespacesAndNewlines), !string.isEmpty else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chatToolbarContent: some View {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
if let officialName = officialDisplayName {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
nameText(officialName, weight: .semibold)
|
||||||
|
|
||||||
|
Image(systemName: "checkmark.seal.fill")
|
||||||
|
.foregroundColor(Color.accentColor)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nameText(title, weight: .semibold)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(offlineStatusText)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let url = avatarUrl,
|
||||||
|
let fileId = chat.chatData?.avatars?.current?.fileId,
|
||||||
|
let userId = currentUserId {
|
||||||
|
CachedAvatarView(url: url, fileId: fileId, userId: userId) {
|
||||||
|
headerPlaceholderAvatar
|
||||||
|
}
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(width: headerAvatarSize, height: headerAvatarSize)
|
||||||
|
.clipShape(Circle())
|
||||||
|
} else {
|
||||||
|
headerPlaceholderAvatar
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func nameText(_ text: String, weight: Font.Weight) -> some View {
|
||||||
|
Group {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
Text(text)
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(weight)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
.strikethrough(isDeletedUser, color: Color.secondary)
|
||||||
|
} else {
|
||||||
|
Text(text)
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(weight)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var headerPlaceholderAvatar: some View {
|
||||||
|
Circle()
|
||||||
|
.fill(avatarBackgroundColor)
|
||||||
|
.frame(width: headerAvatarSize, height: headerAvatarSize)
|
||||||
|
.overlay(
|
||||||
|
Group {
|
||||||
|
if isDeletedUser {
|
||||||
|
Image(systemName: deletedUserSymbolName)
|
||||||
|
.symbolRenderingMode(.hierarchical)
|
||||||
|
.font(.system(size: headerAvatarSize * 0.45, weight: .semibold))
|
||||||
|
.foregroundColor(avatarTextColor)
|
||||||
|
} else {
|
||||||
|
Text(avatarInitial)
|
||||||
|
.font(.system(size: headerAvatarSize * 0.5, weight: .semibold))
|
||||||
|
.foregroundColor(avatarTextColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user