added Sendable and other corrections

This commit is contained in:
Wilhelm Oks 2024-12-22 17:24:48 +01:00
parent d782a2fcf5
commit 3824735fe1
26 changed files with 119 additions and 49 deletions

View File

@ -1,6 +1,6 @@
/// Represents an error coming directly from the devrant API. /// Represents an error coming directly from the devrant API.
public struct DevRantApiError: Swift.Error { public struct DevRantApiError: Swift.Error {
let message: String public let message: String
} }
public extension DevRantApiError { public extension DevRantApiError {

View File

@ -1,5 +1,5 @@
/// An image that the user has uploaded for his rant or comment. /// 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 url: String
public let width: Int public let width: Int
public let height: Int public let height: Int

View File

@ -1,6 +1,6 @@
import Foundation import Foundation
public struct AuthToken: Hashable { public struct AuthToken: Hashable, Sendable {
public let id: Int public let id: Int
public let key: String public let key: String
public let expireTime: Date public let expireTime: Date
@ -18,21 +18,21 @@ public struct AuthToken: Hashable {
} }
} }
extension AuthToken { public extension AuthToken {
struct CodingData: Codable { public struct CodingData: Codable {
struct Container: Codable { public struct Container: Codable {
let auth_token: AuthToken.CodingData let auth_token: AuthToken.CodingData
} }
let id: Int public let id: Int
let key: String public let key: String
let expire_time: Int public let expire_time: Int
let user_id: Int public let user_id: Int
} }
} }
extension AuthToken.CodingData { public extension AuthToken.CodingData {
var decoded: AuthToken { public var decoded: AuthToken {
.init( .init(
id: id, id: id,
key: key, 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
)
}
}

View File

@ -1,5 +1,5 @@
public struct Collaboration: Hashable { public struct Collaboration: Hashable, Sendable {
public enum Kind: Int { public enum Kind: Int, Sendable {
case openSourceIdea = 1 case openSourceIdea = 1
case existingOpenSourceProject = 2 case existingOpenSourceProject = 2
case projectIdea = 3 case projectIdea = 3

View File

@ -1,7 +1,7 @@
import Foundation import Foundation
/// A comment posted by a user inside of a rant. /// 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. /// The id of this comment.
public let id: Int public let id: Int

View File

@ -1,5 +1,5 @@
/// Represents the reason for downvoting. /// Represents the reason for downvoting.
public enum DownvoteReason: Int, Hashable { public enum DownvoteReason: Int, Hashable, Sendable {
case notForMe = 0 case notForMe = 0
case repost = 1 case repost = 1
case offensiveOrSpam = 2 case offensiveOrSpam = 2

View File

@ -1,8 +1,8 @@
/// A URL or a user mention link in a rant or comment. /// A URL or a user mention link in a rant or comment.
public struct Link: Hashable { public struct Link: Hashable, Sendable {
public enum Kind { public enum Kind: String, Sendable {
case url case url = "url"
case userMention case userMention = "mention"
} }
public let kind: Kind public let kind: Kind
@ -47,7 +47,7 @@ extension Link {
extension Link.CodingData { extension Link.CodingData {
var decoded: Link { var decoded: Link {
.init( .init(
kind: type == "mention" ? .userMention : .url, kind: .init(rawValue: type) ?? .url,
url: url, url: url,
shortURL: short_url, shortURL: short_url,
title: title, title: title,

View File

@ -1,8 +1,8 @@
import Foundation import Foundation
/// A notification about activities in a rant or a comment. /// A notification about activities in a rant or a comment.
public struct Notification: Hashable, Identifiable { public struct Notification: Hashable, Identifiable, Sendable {
public enum Kind: String { public enum Kind: String, Sendable {
/// An upvote for a rant. /// An upvote for a rant.
case rantUpvote = "content_vote" case rantUpvote = "content_vote"

View File

@ -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,
]
}
}

View File

@ -1,6 +1,6 @@
public extension NotificationFeed { public extension NotificationFeed {
/// Holds numbers of unread notifications for each type of notification. /// 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 /// The total number of unread notifications
public let all: Int public let all: Int

View File

@ -1,5 +1,5 @@
public extension NotificationFeed { public extension NotificationFeed {
struct UserInfo: Hashable { struct UserInfo: Hashable, Sendable {
public let avatar: User.Avatar public let avatar: User.Avatar
public let username: String public let username: String
public let userId: String //TODO: why is this String? The other user ids are Int. public let userId: String //TODO: why is this String? The other user ids are Int.

View File

@ -1,8 +1,8 @@
import Foundation import Foundation
/// Contains a list of all notifications for the logged in user and the numbers of unread notifications. /// Contains a list of all notifications for the logged in user and the numbers of unread notifications.
public struct NotificationFeed: Hashable { public struct NotificationFeed: Hashable, Sendable {
public enum Category: String, CaseIterable { public enum Category: String, CaseIterable, Sendable {
case all = "" case all = ""
case upvotes = "upvotes" case upvotes = "upvotes"
case mentions = "mentions" case mentions = "mentions"

View File

@ -1,5 +1,5 @@
public extension Profile.Content { public extension Profile.Content {
struct Elements: Hashable { struct Elements: Hashable, Sendable {
/// The rants that the user has created. /// The rants that the user has created.
public let rants: [Rant] public let rants: [Rant]

View File

@ -1,5 +1,5 @@
public extension Profile.Content { public extension Profile.Content {
struct Numbers: Hashable { struct Numbers: Hashable, Sendable {
/// The number of rants that the user has created. /// The number of rants that the user has created.
public let rants: Int public let rants: Int

View File

@ -1,5 +1,5 @@
public extension Profile { public extension Profile {
struct Content: Hashable { struct Content: Hashable, Sendable {
public let elements: Elements public let elements: Elements
public let numbers: Numbers public let numbers: Numbers

View File

@ -1,7 +1,7 @@
import Foundation import Foundation
/// Holds information, content and the activity history of a user. /// Holds information, content and the activity history of a user.
public struct Profile: Hashable { public struct Profile: Hashable, Sendable {
/// The user's alias. /// The user's alias.
public let username: String public let username: String
@ -39,7 +39,7 @@ public struct Profile: Hashable {
public let devRantSupporter: Bool public let devRantSupporter: Bool
/// True if the logged in user is subscribed to the user of this profile. /// 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) { 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 self.username = username
@ -59,7 +59,7 @@ public struct Profile: Hashable {
} }
public extension Profile { public extension Profile {
enum ContentType: String { enum ContentType: String, Sendable {
/// All user content. /// All user content.
case all = "all" case all = "all"

View File

@ -1,5 +1,5 @@
public extension Rant { public extension Rant {
public enum Kind: Int { public enum Kind: Int, Sendable {
case rant = 1 case rant = 1
case collaboration = 2 case collaboration = 2
case meme = 3 case meme = 3

View File

@ -1,6 +1,6 @@
public extension Rant { public extension Rant {
/// Holds information about a specific weekly group rant. /// Holds information about a specific weekly group rant.
struct Weekly: Hashable { struct Weekly: Hashable, Sendable {
public let week: Int public let week: Int
public let topic: String public let topic: String
public let date: String public let date: String

View File

@ -1,6 +1,6 @@
import Foundation import Foundation
public struct Rant: Identifiable, Hashable { public struct Rant: Identifiable, Hashable, Sendable {
/// The id of this rant. /// The id of this rant.
public let id: Int public let id: Int
@ -23,7 +23,7 @@ public struct Rant: Identifiable, Hashable {
public let isEdited: Bool public let isEdited: Bool
/// 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 let isFavorite: Bool public var isFavorite: Bool
/// The text contents of this rant. /// The text contents of this rant.
public let text: String public let text: String

View File

@ -1,8 +1,8 @@
public extension RantFeed { public extension RantFeed {
/// Contains information about news given in rant feeds. /// Contains information about news given in rant feeds.
/// - note: This is mostly used for weekly group rants. /// - note: This is mostly used for weekly group rants.
struct News: Hashable, Identifiable { struct News: Hashable, Identifiable, Sendable {
public enum Action: String { public enum Action: String, Sendable {
case groupRant = "grouprant" case groupRant = "grouprant"
case none = "none" case none = "none"
case rant = "rant" case rant = "rant"

View File

@ -1,5 +1,5 @@
/// Contains the list of rants for the logged in user and other random things. /// 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 rants: [Rant]
public let sessionHash: String? public let sessionHash: String?
@ -25,7 +25,7 @@ public struct RantFeed: Hashable {
} }
public extension RantFeed { public extension RantFeed {
enum Sort { enum Sort: Sendable {
/// The devRant algorithm decides what rants appear in the feed. /// The devRant algorithm decides what rants appear in the feed.
case algorithm case algorithm
@ -36,7 +36,7 @@ public extension RantFeed {
case top(range: Range) case top(range: Range)
} }
enum Range { enum Range: Sendable {
/// Rants from the one day. /// Rants from the one day.
case day case day

View File

@ -1,11 +1,13 @@
import Foundation
public extension User { public extension User {
struct Avatar: Hashable { struct Avatar: Hashable, Sendable {
public let colorHex: String public let colorHex: String
public let imageUrlPath: String? public let imageUrlPath: String?
public var imageUrl: String? { public var imageUrl: URL? {
imageUrlPath.flatMap { "https://avatars.devrant.com/\($0)" } imageUrlPath.flatMap { URL(string: "https://avatars.devrant.com/\($0)") }
} }
public init(colorHex: String, imageUrlPath: String?) { public init(colorHex: String, imageUrlPath: String?) {

View File

@ -1,5 +1,5 @@
/// Represents a user. /// Represents a user.
public struct User: Identifiable, Hashable { public struct User: Identifiable, Hashable, Sendable {
public let id: Int public let id: Int
public let name: String public let name: String

View File

@ -1,5 +1,5 @@
/// Represents the different kinds of votes that a rant or comment can have. /// 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. /// A given ++ vote.
case upvoted = 1 case upvoted = 1

View File

@ -1,5 +1,5 @@
/// The weekly item data for the list of weeklies. /// 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. /// The number of the week. The first week starts with 1.
public let week: Int public let week: Int

View File

@ -1,7 +1,7 @@
import Foundation import Foundation
import KreeRequest import KreeRequest
public struct SwiftDevRant { public struct SwiftDevRant { //TODO: rename to something else to not collide with the module name
let request: KreeRequest let request: KreeRequest
let backend = DevRantBackend() let backend = DevRantBackend()