Models WIP: AttachedImage, Link, Rant.Weekly, User.Avatar

This commit is contained in:
Wilhelm Oks 2024-12-11 11:54:12 +01:00
parent 2781bf4bdd
commit 2e4d605ecc
7 changed files with 186 additions and 21 deletions

View File

@ -0,0 +1,30 @@
/// An image that the user has uploaded for his rant or comment.
public struct AttachedImage: Hashable {
public let url: String
public let width: Int
public let height: Int
public init(url: String, width: Int, height: Int) {
self.url = url
self.width = width
self.height = height
}
}
extension AttachedImage {
struct CodingData: Codable {
let url: String
let width: Int
let height: Int
}
}
extension AttachedImage.CodingData {
var decoded: AttachedImage {
.init(
url: url,
width: width,
height: height
)
}
}

View File

@ -4,4 +4,12 @@ public struct Collaboration: Hashable {
public let techStack: String public let techStack: String
public let teamSize: String public let teamSize: String
public let url: String public let url: String
public init(type: String, description: String, techStack: String, teamSize: String, url: String) {
self.type = type
self.description = description
self.techStack = techStack
self.teamSize = teamSize
self.url = url
}
} }

View File

@ -0,0 +1,58 @@
/// A URL or a user mention link in a rant or comment.
public struct Link: Hashable {
public enum Kind {
case url
case userMention
}
public let kind: Kind
/// The full URL.
public let url: String
public let shortURL: String? //TODO: what is this and what is it used for?
/// The url as it is visible in the text of the rant or comment.
public let title: String
/// The starting position of the link in the overall text of the rant or comment.
/// - Important: The devRant API returns offsets for links in byte offsets and not in normalized character offsets. Please take this into account when using these offsets.
public let start: Int?
/// The ending position of the link in the overall text of the rant or comment.
/// - Important: The devRant API returns offsets for links in byte offsets and not in normalized character offsets. Please take this into account when using these offsets.
public let end: Int?
public init(kind: Link.Kind, url: String, shortURL: String?, title: String, start: Int?, end: Int?) {
self.kind = kind
self.url = url
self.shortURL = shortURL
self.title = title
self.start = start
self.end = end
}
}
extension Link {
struct CodingData: Codable {
let type: String
let url: String
let short_url: String
let title: String
let start: Int?
let end: Int?
}
}
extension Link.CodingData {
var decoded: Link {
.init(
kind: type == "mention" ? .userMention : .url,
url: url,
shortURL: short_url,
title: title,
start: start,
end: end
)
}
}

View File

@ -0,0 +1,36 @@
public extension Rant {
/// Holds information about a specific weekly group rant.
struct Weekly: Hashable {
public let week: Int
public let topic: String
public let date: String
public let uiHeight: Int
public init(week: Int, topic: String, date: String, uiHeight: Int) {
self.week = week
self.topic = topic
self.date = date
self.uiHeight = uiHeight
}
}
}
extension Rant.Weekly {
struct CodingData: Codable {
let week: Int
let topic: String
let date: String
let height: Int
}
}
extension Rant.Weekly.CodingData {
var decoded: Rant.Weekly {
.init(
week: week,
topic: topic,
date: date,
uiHeight: height
)
}
}

View File

@ -1,8 +1,6 @@
import Foundation import Foundation
public struct Rant: Identifiable, Hashable { public struct Rant: Identifiable, Hashable {
//TODO: public let weekly: Weekly?
/// The id of this rant. /// The id of this rant.
public let id: Int public let id: Int
@ -15,8 +13,8 @@ public struct Rant: Identifiable, Hashable {
/// The time when this rant was created. /// The time when this rant was created.
public let created: Date public let created: Date
/// If the rant has an image attached to it, a URL of the image will be stored in this. /// The optional image that the user has uploaded for this rant.
//TODO: public let attachedImage: AttachedImage? public let image: AttachedImage?
/// The number of comments that this rant has. /// The number of comments that this rant has.
public let numberOfComments: Int public let numberOfComments: Int
@ -33,17 +31,20 @@ public struct Rant: Identifiable, Hashable {
/// True if this rant has been marked as a favorite by the logged in user. /// True if this rant has been marked as a favorite by the logged in user.
public var isFavorite: Bool public var isFavorite: Bool
/// A url link to this rant. /// A URL link to this rant.
public let link: String? public let linkToRant: String?
/// If the rant includes URLs in the text, those that were successfully parsed by the server will be in this array. /// The URLs and user mentions inside of the text of this rant.
//TODO: public var links: [Link]? public var linksInText: [Link]
/// Holds information about the weekly topic if this rant is of type weekly.
public let weekly: Weekly?
/// Holds information about the collaboration project if this rant is of type collaboration. /// Holds information about the collaboration project if this rant is of type collaboration.
let collaboration: Collaboration? public let collaboration: Collaboration?
/// The user who wrote this rant. /// The user who wrote this rant.
let author: User public let author: User
} }
extension Rant { extension Rant {
@ -52,15 +53,15 @@ extension Rant {
let text: String let text: String
let score: Int let score: Int
let created_time: Int let created_time: Int
//TODO: let attachedImage: AttachedImage? let attached_image: AttachedImage.CodingData?
let num_comments: Int let num_comments: Int
let tags: [String] let tags: [String]
let vote_state: Int let vote_state: Int
//TODO: let weekly: Weekly?
let edited: Bool let edited: Bool
let favorited: Int? let favorited: Int?
let link: String? let link: String?
//TODO: let links: [Link] let links: [Link.CodingData]?
let weekly: Weekly.CodingData?
let c_type_long: String? let c_type_long: String?
let c_description: String? let c_description: String?
let c_tech_stack: String? let c_tech_stack: String?
@ -69,8 +70,8 @@ extension Rant {
let user_id: Int let user_id: Int
let user_username: String let user_username: String
let user_score: Int let user_score: Int
//TODO: let user_avatar: UserAvatar let user_avatar: User.Avatar.CodingData
//TODO: let user_avatar_lg: UserAvatar let user_avatar_lg: User.Avatar.CodingData
let user_dpp: Int? let user_dpp: Int?
} }
} }
@ -82,18 +83,23 @@ extension Rant.CodingData {
text: text, text: text,
score: score, score: score,
created: Date(timeIntervalSince1970: TimeInterval(created_time)), created: Date(timeIntervalSince1970: TimeInterval(created_time)),
image: attached_image?.decoded,
numberOfComments: num_comments, numberOfComments: num_comments,
tags: tags, tags: tags,
voteState: .init(rawValue: vote_state) ?? .unvoted, voteState: .init(rawValue: vote_state) ?? .unvoted,
isEdited: edited, isEdited: edited,
isFavorite: (favorited ?? 0) != 0, isFavorite: (favorited ?? 0) != 0,
link: link, linkToRant: link,
linksInText: links?.map(\.decoded) ?? [],
weekly: weekly?.decoded,
collaboration: decodedCollaboration, collaboration: decodedCollaboration,
author: .init( author: .init(
id: user_id, id: user_id,
name: user_username, name: user_username,
score: user_score, score: user_score,
devRantSupporter: (user_dpp ?? 0) != 0 devRantSupporter: (user_dpp ?? 0) != 0,
avatar: user_avatar.decoded,
avatarLarge: user_avatar_lg.decoded
) )
) )
} }

View File

@ -0,0 +1,27 @@
public extension User {
struct Avatar: Hashable {
public let colorHex: String
public let imageUrlPath: String?
public var imageUrl: String? {
imageUrlPath.flatMap { "https://avatars.devrant.com/\($0)" }
}
}
}
extension User.Avatar {
struct CodingData: Codable {
let b: String
let i: String?
}
}
extension User.Avatar.CodingData {
var decoded: User.Avatar {
.init(
colorHex: b,
imageUrlPath: i
)
}
}

View File

@ -4,9 +4,9 @@ public struct User: Identifiable, Hashable {
public let score: Int public let score: Int
public let devRantSupporter: Bool public let devRantSupporter: Bool
/// The author's avatar, can be used optimally for small portraits of the user. /// A small avatar for the rant views and comment views.
//TODO: public let userAvatar: UserAvatar public let avatar: Avatar
/// A larger version of the author's avatar, can be used optimally for profile screens. /// A large avatar for the profile view.
//TODO: public let userAvatarLarge: UserAvatar public let avatarLarge: Avatar
} }