diff --git a/Sources/SwiftDevRant/DevRant/DevRantApiError.swift b/Sources/SwiftDevRant/DevRant/DevRantApiError.swift index 57e9921..c1226dd 100644 --- a/Sources/SwiftDevRant/DevRant/DevRantApiError.swift +++ b/Sources/SwiftDevRant/DevRant/DevRantApiError.swift @@ -1,6 +1,6 @@ /// Represents an error coming directly from the devrant API. public struct DevRantApiError: Swift.Error { - let message: String + public let message: String } public extension DevRantApiError { diff --git a/Sources/SwiftDevRant/Models/AttachedImage.swift b/Sources/SwiftDevRant/Models/AttachedImage.swift index 80aa375..0bb03df 100644 --- a/Sources/SwiftDevRant/Models/AttachedImage.swift +++ b/Sources/SwiftDevRant/Models/AttachedImage.swift @@ -1,5 +1,5 @@ /// An image that the user has uploaded for his rant or comment. -public struct AttachedImage: Hashable { +public struct AttachedImage: Hashable, Sendable { public let url: String public let width: Int public let height: Int diff --git a/Sources/SwiftDevRant/Models/AuthToken.swift b/Sources/SwiftDevRant/Models/AuthToken.swift index b3c4a64..6a79832 100644 --- a/Sources/SwiftDevRant/Models/AuthToken.swift +++ b/Sources/SwiftDevRant/Models/AuthToken.swift @@ -1,6 +1,6 @@ import Foundation -public struct AuthToken: Hashable { +public struct AuthToken: Hashable, Sendable { public let id: Int public let key: String public let expireTime: Date @@ -18,21 +18,21 @@ public struct AuthToken: Hashable { } } -extension AuthToken { - struct CodingData: Codable { - struct Container: Codable { +public extension AuthToken { + public struct CodingData: Codable { + public struct Container: Codable { let auth_token: AuthToken.CodingData } - let id: Int - let key: String - let expire_time: Int - let user_id: Int + public let id: Int + public let key: String + public let expire_time: Int + public let user_id: Int } } -extension AuthToken.CodingData { - var decoded: AuthToken { +public extension AuthToken.CodingData { + public var decoded: AuthToken { .init( id: id, key: key, @@ -41,3 +41,14 @@ extension AuthToken.CodingData { ) } } + +public extension AuthToken { + public var encoded: AuthToken.CodingData { + .init( + id: id, + key: key, + expire_time: Int(expireTime.timeIntervalSince1970), + user_id: userId + ) + } +} diff --git a/Sources/SwiftDevRant/Models/Collaboration.swift b/Sources/SwiftDevRant/Models/Collaboration.swift index 50ca926..385853c 100644 --- a/Sources/SwiftDevRant/Models/Collaboration.swift +++ b/Sources/SwiftDevRant/Models/Collaboration.swift @@ -1,5 +1,5 @@ -public struct Collaboration: Hashable { - public enum Kind: Int { +public struct Collaboration: Hashable, Sendable { + public enum Kind: Int, Sendable { case openSourceIdea = 1 case existingOpenSourceProject = 2 case projectIdea = 3 diff --git a/Sources/SwiftDevRant/Models/Comment.swift b/Sources/SwiftDevRant/Models/Comment.swift index 5aa743f..647026d 100644 --- a/Sources/SwiftDevRant/Models/Comment.swift +++ b/Sources/SwiftDevRant/Models/Comment.swift @@ -1,7 +1,7 @@ import Foundation /// A comment posted by a user inside of a rant. -public struct Comment: Identifiable, Hashable { +public struct Comment: Identifiable, Hashable, Sendable { /// The id of this comment. public let id: Int diff --git a/Sources/SwiftDevRant/Models/DownvoteReason.swift b/Sources/SwiftDevRant/Models/DownvoteReason.swift index b43aa5a..9b901db 100644 --- a/Sources/SwiftDevRant/Models/DownvoteReason.swift +++ b/Sources/SwiftDevRant/Models/DownvoteReason.swift @@ -1,5 +1,5 @@ /// Represents the reason for downvoting. -public enum DownvoteReason: Int, Hashable { +public enum DownvoteReason: Int, Hashable, Sendable { case notForMe = 0 case repost = 1 case offensiveOrSpam = 2 diff --git a/Sources/SwiftDevRant/Models/Link.swift b/Sources/SwiftDevRant/Models/Link.swift index 4c5a648..cd08271 100644 --- a/Sources/SwiftDevRant/Models/Link.swift +++ b/Sources/SwiftDevRant/Models/Link.swift @@ -1,8 +1,8 @@ /// A URL or a user mention link in a rant or comment. -public struct Link: Hashable { - public enum Kind { - case url - case userMention +public struct Link: Hashable, Sendable { + public enum Kind: String, Sendable { + case url = "url" + case userMention = "mention" } public let kind: Kind @@ -47,7 +47,7 @@ extension Link { extension Link.CodingData { var decoded: Link { .init( - kind: type == "mention" ? .userMention : .url, + kind: .init(rawValue: type) ?? .url, url: url, shortURL: short_url, title: title, diff --git a/Sources/SwiftDevRant/Models/Notification.swift b/Sources/SwiftDevRant/Models/Notification.swift index e5334aa..4baf42e 100644 --- a/Sources/SwiftDevRant/Models/Notification.swift +++ b/Sources/SwiftDevRant/Models/Notification.swift @@ -1,8 +1,8 @@ import Foundation /// A notification about activities in a rant or a comment. -public struct Notification: Hashable, Identifiable { - public enum Kind: String { +public struct Notification: Hashable, Identifiable, Sendable { + public enum Kind: String, Sendable { /// An upvote for a rant. case rantUpvote = "content_vote" diff --git a/Sources/SwiftDevRant/Models/NotificationFeed+Mapping.swift b/Sources/SwiftDevRant/Models/NotificationFeed+Mapping.swift new file mode 100644 index 0000000..acd620f --- /dev/null +++ b/Sources/SwiftDevRant/Models/NotificationFeed+Mapping.swift @@ -0,0 +1,57 @@ +import Foundation + +public extension NotificationFeed { + public struct MappedNotificationItem: Hashable, Sendable { + public let rantId: Rant.ID + public let commentId: Comment.ID? + public let userId: Int + public let userAvatar: User.Avatar + public let userName: String + public let notificationKind: Notification.Kind + public let created: Date + public let isRead: Bool + + public init(rantId: Rant.ID, commentId: Comment.ID?, userId: Int, userAvatar: User.Avatar, userName: String, notificationKind: Notification.Kind, created: Date, isRead: Bool) { + self.rantId = rantId + self.commentId = commentId + self.userId = userId + self.userAvatar = userAvatar + self.userName = userName + self.notificationKind = notificationKind + self.created = created + self.isRead = isRead + } + } + + public var mappedItems: [MappedNotificationItem] { + notifications.map { notification in + let rantId = notification.rantId + let commentId = notification.commentId + let userId = notification.userId + let userInfo = userInfos.first { $0.userId == String(userId) } + let userAvatar = userInfo?.avatar ?? .init(colorHex: "cccccc", imageUrlPath: nil) + let userName = userInfo?.username ?? "" + + return MappedNotificationItem( + rantId: rantId, + commentId: commentId, + userId: userId, + userAvatar: userAvatar, + userName: userName, + notificationKind: notification.kind, + created: notification.created, + isRead: notification.read + ) + } + } + + public var unreadByCategory: [NotificationFeed.Category: Int] { + [ + .all: unreadNumbers.all, + .upvotes: unreadNumbers.upvotes, + .mentions: unreadNumbers.mentions, + .comments: unreadNumbers.comments, + .subscriptions: unreadNumbers.subscriptions, + ] + } +} diff --git a/Sources/SwiftDevRant/Models/NotificationFeed.UnreadNumbers.swift b/Sources/SwiftDevRant/Models/NotificationFeed.UnreadNumbers.swift index bd4ac99..8f54654 100644 --- a/Sources/SwiftDevRant/Models/NotificationFeed.UnreadNumbers.swift +++ b/Sources/SwiftDevRant/Models/NotificationFeed.UnreadNumbers.swift @@ -1,6 +1,6 @@ public extension NotificationFeed { /// Holds numbers of unread notifications for each type of notification. - struct UnreadNumbers: Decodable, Hashable { + struct UnreadNumbers: Decodable, Hashable, Sendable { /// The total number of unread notifications public let all: Int diff --git a/Sources/SwiftDevRant/Models/NotificationFeed.UserInfo.swift b/Sources/SwiftDevRant/Models/NotificationFeed.UserInfo.swift index d4eaf27..42785fc 100644 --- a/Sources/SwiftDevRant/Models/NotificationFeed.UserInfo.swift +++ b/Sources/SwiftDevRant/Models/NotificationFeed.UserInfo.swift @@ -1,5 +1,5 @@ public extension NotificationFeed { - struct UserInfo: Hashable { + struct UserInfo: Hashable, Sendable { public let avatar: User.Avatar public let username: String public let userId: String //TODO: why is this String? The other user ids are Int. diff --git a/Sources/SwiftDevRant/Models/NotificationFeed.swift b/Sources/SwiftDevRant/Models/NotificationFeed.swift index 93914f9..988d559 100644 --- a/Sources/SwiftDevRant/Models/NotificationFeed.swift +++ b/Sources/SwiftDevRant/Models/NotificationFeed.swift @@ -1,8 +1,8 @@ import Foundation /// Contains a list of all notifications for the logged in user and the numbers of unread notifications. -public struct NotificationFeed: Hashable { - public enum Category: String, CaseIterable { +public struct NotificationFeed: Hashable, Sendable { + public enum Category: String, CaseIterable, Sendable { case all = "" case upvotes = "upvotes" case mentions = "mentions" diff --git a/Sources/SwiftDevRant/Models/Profile.Content.Elements.swift b/Sources/SwiftDevRant/Models/Profile.Content.Elements.swift index 1919870..3241b88 100644 --- a/Sources/SwiftDevRant/Models/Profile.Content.Elements.swift +++ b/Sources/SwiftDevRant/Models/Profile.Content.Elements.swift @@ -1,5 +1,5 @@ public extension Profile.Content { - struct Elements: Hashable { + struct Elements: Hashable, Sendable { /// The rants that the user has created. public let rants: [Rant] diff --git a/Sources/SwiftDevRant/Models/Profile.Content.Numbers.swift b/Sources/SwiftDevRant/Models/Profile.Content.Numbers.swift index 40f8735..7f9aac2 100644 --- a/Sources/SwiftDevRant/Models/Profile.Content.Numbers.swift +++ b/Sources/SwiftDevRant/Models/Profile.Content.Numbers.swift @@ -1,5 +1,5 @@ public extension Profile.Content { - struct Numbers: Hashable { + struct Numbers: Hashable, Sendable { /// The number of rants that the user has created. public let rants: Int diff --git a/Sources/SwiftDevRant/Models/Profile.Content.swift b/Sources/SwiftDevRant/Models/Profile.Content.swift index 2e32599..4b82f28 100644 --- a/Sources/SwiftDevRant/Models/Profile.Content.swift +++ b/Sources/SwiftDevRant/Models/Profile.Content.swift @@ -1,5 +1,5 @@ public extension Profile { - struct Content: Hashable { + struct Content: Hashable, Sendable { public let elements: Elements public let numbers: Numbers diff --git a/Sources/SwiftDevRant/Models/Profile.swift b/Sources/SwiftDevRant/Models/Profile.swift index 43973ca..3d7c728 100644 --- a/Sources/SwiftDevRant/Models/Profile.swift +++ b/Sources/SwiftDevRant/Models/Profile.swift @@ -1,7 +1,7 @@ import Foundation /// Holds information, content and the activity history of a user. -public struct Profile: Hashable { +public struct Profile: Hashable, Sendable { /// The user's alias. public let username: String @@ -39,7 +39,7 @@ public struct Profile: Hashable { public let devRantSupporter: Bool /// True if the logged in user is subscribed to the user of this profile. - public let subscribed: Bool //TODO: where is this set? It's not in the json data of the profile + public var subscribed: Bool //TODO: where is this set? It's not in the json data of the profile public init(username: String, score: Int, created: Date, about: String?, location: String?, skills: String?, github: String?, website: String?, content: Profile.Content, avatarLarge: User.Avatar, avatarSmall: User.Avatar, devRantSupporter: Bool, subscribed: Bool) { self.username = username @@ -59,7 +59,7 @@ public struct Profile: Hashable { } public extension Profile { - enum ContentType: String { + enum ContentType: String, Sendable { /// All user content. case all = "all" diff --git a/Sources/SwiftDevRant/Models/Rant.Kind.swift b/Sources/SwiftDevRant/Models/Rant.Kind.swift index 36ef6fe..15757c2 100644 --- a/Sources/SwiftDevRant/Models/Rant.Kind.swift +++ b/Sources/SwiftDevRant/Models/Rant.Kind.swift @@ -1,5 +1,5 @@ public extension Rant { - public enum Kind: Int { + public enum Kind: Int, Sendable { case rant = 1 case collaboration = 2 case meme = 3 diff --git a/Sources/SwiftDevRant/Models/Rant.Weekly.swift b/Sources/SwiftDevRant/Models/Rant.Weekly.swift index 5c2a55f..c763cb0 100644 --- a/Sources/SwiftDevRant/Models/Rant.Weekly.swift +++ b/Sources/SwiftDevRant/Models/Rant.Weekly.swift @@ -1,6 +1,6 @@ public extension Rant { /// Holds information about a specific weekly group rant. - struct Weekly: Hashable { + struct Weekly: Hashable, Sendable { public let week: Int public let topic: String public let date: String diff --git a/Sources/SwiftDevRant/Models/Rant.swift b/Sources/SwiftDevRant/Models/Rant.swift index 3c14323..51a5007 100644 --- a/Sources/SwiftDevRant/Models/Rant.swift +++ b/Sources/SwiftDevRant/Models/Rant.swift @@ -1,6 +1,6 @@ import Foundation -public struct Rant: Identifiable, Hashable { +public struct Rant: Identifiable, Hashable, Sendable { /// The id of this rant. public let id: Int @@ -23,7 +23,7 @@ public struct Rant: Identifiable, Hashable { public let isEdited: Bool /// True if this rant has been marked as a favorite by the logged in user. - public let isFavorite: Bool + public var isFavorite: Bool /// The text contents of this rant. public let text: String diff --git a/Sources/SwiftDevRant/Models/RantFeed.News.swift b/Sources/SwiftDevRant/Models/RantFeed.News.swift index 29de217..7136956 100644 --- a/Sources/SwiftDevRant/Models/RantFeed.News.swift +++ b/Sources/SwiftDevRant/Models/RantFeed.News.swift @@ -1,8 +1,8 @@ public extension RantFeed { /// Contains information about news given in rant feeds. /// - note: This is mostly used for weekly group rants. - struct News: Hashable, Identifiable { - public enum Action: String { + struct News: Hashable, Identifiable, Sendable { + public enum Action: String, Sendable { case groupRant = "grouprant" case none = "none" case rant = "rant" diff --git a/Sources/SwiftDevRant/Models/RantFeed.swift b/Sources/SwiftDevRant/Models/RantFeed.swift index 2376cd2..211c57e 100644 --- a/Sources/SwiftDevRant/Models/RantFeed.swift +++ b/Sources/SwiftDevRant/Models/RantFeed.swift @@ -1,5 +1,5 @@ /// Contains the list of rants for the logged in user and other random things. -public struct RantFeed: Hashable { +public struct RantFeed: Hashable, Sendable { public let rants: [Rant] public let sessionHash: String? @@ -25,7 +25,7 @@ public struct RantFeed: Hashable { } public extension RantFeed { - enum Sort { + enum Sort: Sendable { /// The devRant algorithm decides what rants appear in the feed. case algorithm @@ -36,7 +36,7 @@ public extension RantFeed { case top(range: Range) } - enum Range { + enum Range: Sendable { /// Rants from the one day. case day diff --git a/Sources/SwiftDevRant/Models/User.Avatar.swift b/Sources/SwiftDevRant/Models/User.Avatar.swift index 8349165..74e2292 100644 --- a/Sources/SwiftDevRant/Models/User.Avatar.swift +++ b/Sources/SwiftDevRant/Models/User.Avatar.swift @@ -1,11 +1,13 @@ +import Foundation + public extension User { - struct Avatar: Hashable { + struct Avatar: Hashable, Sendable { public let colorHex: String public let imageUrlPath: String? - public var imageUrl: String? { - imageUrlPath.flatMap { "https://avatars.devrant.com/\($0)" } + public var imageUrl: URL? { + imageUrlPath.flatMap { URL(string: "https://avatars.devrant.com/\($0)") } } public init(colorHex: String, imageUrlPath: String?) { diff --git a/Sources/SwiftDevRant/Models/User.swift b/Sources/SwiftDevRant/Models/User.swift index a6344f8..6cec0d7 100644 --- a/Sources/SwiftDevRant/Models/User.swift +++ b/Sources/SwiftDevRant/Models/User.swift @@ -1,5 +1,5 @@ /// Represents a user. -public struct User: Identifiable, Hashable { +public struct User: Identifiable, Hashable, Sendable { public let id: Int public let name: String diff --git a/Sources/SwiftDevRant/Models/VoteState.swift b/Sources/SwiftDevRant/Models/VoteState.swift index 829c5a1..7db1ea9 100644 --- a/Sources/SwiftDevRant/Models/VoteState.swift +++ b/Sources/SwiftDevRant/Models/VoteState.swift @@ -1,5 +1,5 @@ /// Represents the different kinds of votes that a rant or comment can have. -public enum VoteState: Int, Hashable { +public enum VoteState: Int, Hashable, Sendable { /// A given ++ vote. case upvoted = 1 diff --git a/Sources/SwiftDevRant/Models/Weekly.swift b/Sources/SwiftDevRant/Models/Weekly.swift index aed2b63..82f3347 100644 --- a/Sources/SwiftDevRant/Models/Weekly.swift +++ b/Sources/SwiftDevRant/Models/Weekly.swift @@ -1,5 +1,5 @@ /// The weekly item data for the list of weeklies. -public struct Weekly: Hashable, Identifiable { +public struct Weekly: Hashable, Identifiable, Sendable { /// The number of the week. The first week starts with 1. public let week: Int diff --git a/Sources/SwiftDevRant/SwiftDevRant.swift b/Sources/SwiftDevRant/SwiftDevRant.swift index 3268838..ee95c20 100644 --- a/Sources/SwiftDevRant/SwiftDevRant.swift +++ b/Sources/SwiftDevRant/SwiftDevRant.swift @@ -1,7 +1,7 @@ import Foundation import KreeRequest -public struct SwiftDevRant { +public struct SwiftDevRant { //TODO: rename to something else to not collide with the module name let request: KreeRequest let backend = DevRantBackend()