add avtars

This commit is contained in:
cheykrym 2025-12-09 22:55:46 +03:00
parent 520f63f74a
commit fc7561a9c2
2 changed files with 71 additions and 23 deletions

View File

@ -167,6 +167,7 @@ struct ChatProfile: Decodable {
let bio: String? let bio: String?
let lastSeen: Int? let lastSeen: Int?
let createdAt: Date? let createdAt: Date?
let avatars: Avatars?
let stories: [JSONValue] let stories: [JSONValue]
let permissions: ChatPermissions? let permissions: ChatPermissions?
let profilePermissions: ChatProfilePermissions? let profilePermissions: ChatProfilePermissions?
@ -181,6 +182,7 @@ struct ChatProfile: Decodable {
case bio case bio
case lastSeen case lastSeen
case createdAt case createdAt
case avatars
case stories case stories
case permissions case permissions
case profilePermissions case profilePermissions
@ -198,6 +200,7 @@ struct ChatProfile: Decodable {
self.bio = try container.decodeIfPresent(String.self, forKey: .bio) self.bio = try container.decodeIfPresent(String.self, forKey: .bio)
self.lastSeen = try container.decodeIfPresent(Int.self, forKey: .lastSeen) self.lastSeen = try container.decodeIfPresent(Int.self, forKey: .lastSeen)
self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt) self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt)
self.avatars = try container.decodeIfPresent(Avatars.self, forKey: .avatars)
self.stories = try container.decodeIfPresent([JSONValue].self, forKey: .stories) ?? [] self.stories = try container.decodeIfPresent([JSONValue].self, forKey: .stories) ?? []
self.permissions = try container.decodeIfPresent(ChatPermissions.self, forKey: .permissions) self.permissions = try container.decodeIfPresent(ChatPermissions.self, forKey: .permissions)
self.profilePermissions = try container.decodeIfPresent(ChatProfilePermissions.self, forKey: .profilePermissions) self.profilePermissions = try container.decodeIfPresent(ChatProfilePermissions.self, forKey: .profilePermissions)
@ -217,6 +220,7 @@ extension ChatProfile {
bio: String? = nil, bio: String? = nil,
lastSeen: Int? = nil, lastSeen: Int? = nil,
createdAt: Date? = nil, createdAt: Date? = nil,
avatars: Avatars? = nil,
stories: [JSONValue] = [], stories: [JSONValue] = [],
permissions: ChatPermissions? = nil, permissions: ChatPermissions? = nil,
profilePermissions: ChatProfilePermissions? = nil, profilePermissions: ChatProfilePermissions? = nil,
@ -230,6 +234,7 @@ extension ChatProfile {
self.bio = bio self.bio = bio
self.lastSeen = lastSeen self.lastSeen = lastSeen
self.createdAt = createdAt self.createdAt = createdAt
self.avatars = avatars
self.stories = stories self.stories = stories
self.permissions = permissions self.permissions = permissions
self.profilePermissions = profilePermissions self.profilePermissions = profilePermissions
@ -238,6 +243,19 @@ extension ChatProfile {
} }
} }
struct AvatarInfo: Decodable {
let fileId: String
let mime: String?
let size: Int?
let width: Int?
let height: Int?
let createdAt: Date?
}
struct Avatars: Decodable {
let current: AvatarInfo?
}
struct ChatPermissions: Decodable { struct ChatPermissions: Decodable {
let youCanSendMessage: Bool let youCanSendMessage: Bool
let youCanPublicInvitePermission: Bool let youCanPublicInvitePermission: Bool

View File

@ -1024,25 +1024,42 @@ private struct ChatRowView: View {
return message.isViewed == true ? Color.accentColor : Color.secondary return message.isViewed == true ? Color.accentColor : Color.secondary
} }
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)")
}
var body: some View { var body: some View {
HStack(spacing: 12) { HStack(spacing: 12) {
Circle() if #available(iOS 15.0, *) {
.fill(avatarBackgroundColor) if let url = avatarUrl {
AsyncImage(url: url) { phase in
switch phase {
case .empty:
placeholderAvatar
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
case .failure:
placeholderAvatar
@unknown default:
placeholderAvatar
}
}
.frame(width: avatarSize, height: avatarSize) .frame(width: avatarSize, height: avatarSize)
.overlay(
Group {
if isDeletedUser {
Image(systemName: deletedUserSymbolName)
.symbolRenderingMode(.hierarchical)
.font(.system(size: avatarSize * 0.45, weight: .semibold))
.foregroundColor(avatarTextColor)
} else { } else {
Text(initial) placeholderAvatar
.font(.system(size: avatarSize * 0.5, weight: .semibold))
.foregroundColor(avatarTextColor)
} }
} else {
placeholderAvatar
} }
)
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
if let officialName = officialDisplayName { if let officialName = officialDisplayName {
@ -1066,14 +1083,6 @@ private struct ChatRowView: View {
.foregroundColor(Color.accentColor) .foregroundColor(Color.accentColor)
.font(.caption) .font(.caption)
} }
// if let login = loginDisplay {
// Text(login)
// .font(.footnote)
// .foregroundColor(.secondary)
// .lineLimit(1)
// .truncationMode(.tail)
// }
} else { } else {
if #available(iOS 16.0, *) { if #available(iOS 16.0, *) {
Text(title) Text(title)
@ -1131,6 +1140,27 @@ private struct ChatRowView: View {
.padding(.vertical, 8) .padding(.vertical, 8)
} }
@ViewBuilder
private var placeholderAvatar: some View {
Circle()
.fill(avatarBackgroundColor)
.frame(width: avatarSize, height: avatarSize)
.overlay(
Group {
if isDeletedUser {
Image(systemName: deletedUserSymbolName)
.symbolRenderingMode(.hierarchical)
.font(.system(size: avatarSize * 0.45, weight: .semibold))
.foregroundColor(avatarTextColor)
} else {
Text(initial)
.font(.system(size: avatarSize * 0.5, weight: .semibold))
.foregroundColor(avatarTextColor)
}
}
)
}
private static func formattedTimestamp(for date: Date) -> String { private static func formattedTimestamp(for date: Date) -> String {
let calendar = Calendar.current let calendar = Calendar.current
let locale = Locale.current let locale = Locale.current