diff --git a/yobble/Network/ChatModels.swift b/yobble/Network/ChatModels.swift index 1ceef01..8b575b4 100644 --- a/yobble/Network/ChatModels.swift +++ b/yobble/Network/ChatModels.swift @@ -167,6 +167,7 @@ struct ChatProfile: Decodable { let bio: String? let lastSeen: Int? let createdAt: Date? + let avatars: Avatars? let stories: [JSONValue] let permissions: ChatPermissions? let profilePermissions: ChatProfilePermissions? @@ -181,6 +182,7 @@ struct ChatProfile: Decodable { case bio case lastSeen case createdAt + case avatars case stories case permissions case profilePermissions @@ -198,6 +200,7 @@ struct ChatProfile: Decodable { self.bio = try container.decodeIfPresent(String.self, forKey: .bio) self.lastSeen = try container.decodeIfPresent(Int.self, forKey: .lastSeen) 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.permissions = try container.decodeIfPresent(ChatPermissions.self, forKey: .permissions) self.profilePermissions = try container.decodeIfPresent(ChatProfilePermissions.self, forKey: .profilePermissions) @@ -217,6 +220,7 @@ extension ChatProfile { bio: String? = nil, lastSeen: Int? = nil, createdAt: Date? = nil, + avatars: Avatars? = nil, stories: [JSONValue] = [], permissions: ChatPermissions? = nil, profilePermissions: ChatProfilePermissions? = nil, @@ -230,6 +234,7 @@ extension ChatProfile { self.bio = bio self.lastSeen = lastSeen self.createdAt = createdAt + self.avatars = avatars self.stories = stories self.permissions = permissions 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 { let youCanSendMessage: Bool let youCanPublicInvitePermission: Bool diff --git a/yobble/Views/Tab/ChatsTab.swift b/yobble/Views/Tab/ChatsTab.swift index 3826582..308800c 100644 --- a/yobble/Views/Tab/ChatsTab.swift +++ b/yobble/Views/Tab/ChatsTab.swift @@ -1023,26 +1023,43 @@ private struct ChatRowView: View { guard let message = chat.lastMessage else { return .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 { HStack(spacing: 12) { - 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) + if #available(iOS 15.0, *) { + 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) + } else { + placeholderAvatar + } + } else { + placeholderAvatar + } VStack(alignment: .leading, spacing: 4) { if let officialName = officialDisplayName { @@ -1066,14 +1083,6 @@ private struct ChatRowView: View { .foregroundColor(Color.accentColor) .font(.caption) } - -// if let login = loginDisplay { -// Text(login) -// .font(.footnote) -// .foregroundColor(.secondary) -// .lineLimit(1) -// .truncationMode(.tail) -// } } else { if #available(iOS 16.0, *) { Text(title) @@ -1130,6 +1139,27 @@ private struct ChatRowView: View { } .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 { let calendar = Calendar.current