366 lines
9.6 KiB
Swift
366 lines
9.6 KiB
Swift
// Models.swift
|
|
// WatchRunner Watch App
|
|
//
|
|
// Created by LittleSheep on 2025/10/29.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// MARK: - Models
|
|
|
|
struct AppToken: Codable {
|
|
let token: String
|
|
}
|
|
|
|
struct SnActivity: Codable, Identifiable {
|
|
let id: String
|
|
let type: String
|
|
let data: ActivityData?
|
|
let createdAt: Date
|
|
}
|
|
|
|
enum ActivityData: Codable {
|
|
case post(SnPost)
|
|
case discovery(DiscoveryData)
|
|
case unknown
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
if let post = try? container.decode(SnPost.self) {
|
|
self = .post(post)
|
|
return
|
|
}
|
|
if let discoveryData = try? container.decode(DiscoveryData.self) {
|
|
self = .discovery(discoveryData)
|
|
return
|
|
}
|
|
self = .unknown
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
// Not needed for decoding
|
|
}
|
|
}
|
|
|
|
struct SnPost: Codable, Identifiable {
|
|
let id: String
|
|
let title: String?
|
|
let content: String?
|
|
let publisher: SnPublisher
|
|
let attachments: [SnCloudFile]
|
|
let tags: [SnPostTag]
|
|
}
|
|
|
|
struct DiscoveryData: Codable {
|
|
let items: [DiscoveryItem]
|
|
}
|
|
|
|
struct DiscoveryItem: Codable, Identifiable {
|
|
var id = UUID()
|
|
let type: String
|
|
let data: DiscoveryItemData
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case type, data
|
|
}
|
|
}
|
|
|
|
enum DiscoveryItemData: Codable {
|
|
case realm(SnRealm)
|
|
case publisher(SnPublisher)
|
|
case article(SnWebArticle)
|
|
case unknown
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
if let realm = try? container.decode(SnRealm.self) {
|
|
self = .realm(realm)
|
|
return
|
|
}
|
|
if let publisher = try? container.decode(SnPublisher.self) {
|
|
self = .publisher(publisher)
|
|
return
|
|
}
|
|
if let article = try? container.decode(SnWebArticle.self) {
|
|
self = .article(article)
|
|
return
|
|
}
|
|
self = .unknown
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
// Not needed for decoding
|
|
}
|
|
}
|
|
|
|
struct SnRealm: Codable, Identifiable {
|
|
let id: String
|
|
let name: String
|
|
let description: String?
|
|
}
|
|
|
|
struct SnPublisher: Codable, Identifiable {
|
|
let id: String
|
|
let name: String
|
|
let nick: String?
|
|
let description: String?
|
|
let picture: SnCloudFile?
|
|
}
|
|
|
|
struct SnCloudFile: Codable, Identifiable {
|
|
let id: String
|
|
let mimeType: String?
|
|
}
|
|
|
|
struct SnPostTag: Codable, Identifiable {
|
|
let id: String
|
|
let slug: String
|
|
let name: String?
|
|
}
|
|
|
|
struct SnWebArticle: Codable, Identifiable {
|
|
let id: String
|
|
let title: String
|
|
let url: String
|
|
}
|
|
|
|
struct SnNotification: Codable, Identifiable {
|
|
let id: String
|
|
let topic: String
|
|
let title: String
|
|
let subtitle: String
|
|
let content: String
|
|
let meta: [String: AnyCodable]?
|
|
let priority: Int
|
|
let viewedAt: Date?
|
|
let accountId: String
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case topic
|
|
case title
|
|
case subtitle
|
|
case content
|
|
case meta
|
|
case priority
|
|
case viewedAt = "viewedAt"
|
|
case accountId = "accountId"
|
|
case createdAt = "createdAt"
|
|
case updatedAt = "updatedAt"
|
|
case deletedAt = "deletedAt"
|
|
}
|
|
}
|
|
|
|
struct AnyCodable: Codable {
|
|
let value: Any
|
|
|
|
init(_ value: Any) {
|
|
self.value = value
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
if let intValue = try? container.decode(Int.self) {
|
|
value = intValue
|
|
} else if let doubleValue = try? container.decode(Double.self) {
|
|
value = doubleValue
|
|
} else if let boolValue = try? container.decode(Bool.self) {
|
|
value = boolValue
|
|
} else if let stringValue = try? container.decode(String.self) {
|
|
value = stringValue
|
|
} else if let arrayValue = try? container.decode([AnyCodable].self) {
|
|
value = arrayValue
|
|
} else if let dictValue = try? container.decode([String: AnyCodable].self) {
|
|
value = dictValue
|
|
} else {
|
|
value = NSNull()
|
|
}
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
switch value {
|
|
case let intValue as Int:
|
|
try container.encode(intValue)
|
|
case let doubleValue as Double:
|
|
try container.encode(doubleValue)
|
|
case let boolValue as Bool:
|
|
try container.encode(boolValue)
|
|
case let stringValue as String:
|
|
try container.encode(stringValue)
|
|
case let arrayValue as [AnyCodable]:
|
|
try container.encode(arrayValue)
|
|
case let dictValue as [String: AnyCodable]:
|
|
try container.encode(dictValue)
|
|
default:
|
|
try container.encodeNil()
|
|
}
|
|
}
|
|
}
|
|
|
|
struct NotificationResponse {
|
|
let notifications: [SnNotification]
|
|
let total: Int
|
|
let hasMore: Bool
|
|
}
|
|
|
|
struct ActivityResponse {
|
|
let activities: [SnActivity]
|
|
let hasMore: Bool
|
|
let nextCursor: String?
|
|
}
|
|
|
|
struct SnAccount: Codable {
|
|
let id: String
|
|
let name: String
|
|
let nick: String
|
|
let profile: SnUserProfile
|
|
let createdAt: Date
|
|
}
|
|
|
|
struct SnUserProfile: Codable {
|
|
let bio: String?
|
|
let picture: SnCloudFile?
|
|
let background: SnCloudFile?
|
|
let level: Int
|
|
let experience: Int
|
|
let levelingProgress: Double
|
|
}
|
|
|
|
struct SnAccountStatus: Codable {
|
|
let id: String
|
|
let attitude: Int
|
|
let isOnline: Bool
|
|
let isInvisible: Bool
|
|
let isNotDisturb: Bool
|
|
let isCustomized: Bool
|
|
let label: String
|
|
let meta: [String: AnyCodable]?
|
|
let clearedAt: Date?
|
|
let accountId: String
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
}
|
|
|
|
// MARK: - Chat Models
|
|
|
|
struct SnChatRoom: Codable, Identifiable {
|
|
let id: String
|
|
let name: String?
|
|
let description: String?
|
|
let type: Int
|
|
let isPublic: Bool
|
|
let isCommunity: Bool
|
|
let picture: SnCloudFile?
|
|
let background: SnCloudFile?
|
|
let realmId: String?
|
|
let realm: SnRealm?
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
let members: [SnChatMember]?
|
|
}
|
|
|
|
struct SnChatMessage: Codable, Identifiable {
|
|
let id: String
|
|
let type: String
|
|
let content: String?
|
|
let nonce: String?
|
|
let meta: [String: AnyCodable]
|
|
let membersMentioned: [String]?
|
|
let editedAt: Date?
|
|
let attachments: [SnCloudFile]
|
|
let reactions: [SnChatReaction]
|
|
let repliedMessageId: String?
|
|
let forwardedMessageId: String?
|
|
let senderId: String
|
|
let sender: SnChatMember
|
|
let chatRoomId: String
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id, type, content, nonce, meta, membersMentioned, editedAt, attachments, reactions, repliedMessageId, forwardedMessageId, senderId, sender, chatRoomId, createdAt, updatedAt, deletedAt
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
id = try container.decode(String.self, forKey: .id)
|
|
type = try container.decode(String.self, forKey: .type)
|
|
content = try container.decodeIfPresent(String.self, forKey: .content)
|
|
nonce = try container.decodeIfPresent(String.self, forKey: .nonce)
|
|
meta = try container.decode([String: AnyCodable].self, forKey: .meta)
|
|
membersMentioned = try container.decodeIfPresent([String].self, forKey: .membersMentioned) ?? []
|
|
editedAt = try container.decodeIfPresent(Date.self, forKey: .editedAt)
|
|
attachments = try container.decode([SnCloudFile].self, forKey: .attachments)
|
|
reactions = try container.decode([SnChatReaction].self, forKey: .reactions)
|
|
repliedMessageId = try container.decodeIfPresent(String.self, forKey: .repliedMessageId)
|
|
forwardedMessageId = try container.decodeIfPresent(String.self, forKey: .forwardedMessageId)
|
|
senderId = try container.decode(String.self, forKey: .senderId)
|
|
sender = try container.decode(SnChatMember.self, forKey: .sender)
|
|
chatRoomId = try container.decode(String.self, forKey: .chatRoomId)
|
|
createdAt = try container.decode(Date.self, forKey: .createdAt)
|
|
updatedAt = try container.decode(Date.self, forKey: .updatedAt)
|
|
deletedAt = try container.decodeIfPresent(Date.self, forKey: .deletedAt)
|
|
}
|
|
}
|
|
|
|
struct SnChatReaction: Codable, Identifiable {
|
|
let id: String
|
|
let messageId: String
|
|
let senderId: String
|
|
let sender: SnChatMember
|
|
let symbol: String
|
|
let attitude: Int
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
}
|
|
|
|
struct SnChatMember: Codable, Identifiable {
|
|
let id: String
|
|
let chatRoomId: String
|
|
let chatRoom: SnChatRoom?
|
|
let accountId: String
|
|
let account: SnAccount
|
|
let nick: String?
|
|
let role: Int
|
|
let notify: Int
|
|
let joinedAt: Date?
|
|
let breakUntil: Date?
|
|
let timeoutUntil: Date?
|
|
let isBot: Bool
|
|
let status: SnAccountStatus?
|
|
let createdAt: Date
|
|
let updatedAt: Date
|
|
let deletedAt: Date?
|
|
}
|
|
|
|
struct SnChatSummary: Codable {
|
|
let unreadCount: Int
|
|
let lastMessage: SnChatMessage?
|
|
}
|
|
|
|
struct ChatRoomsResponse {
|
|
let rooms: [SnChatRoom]
|
|
}
|
|
|
|
struct ChatInvitesResponse {
|
|
let invites: [SnChatMember]
|
|
}
|
|
|
|
struct MessageSyncResponse: Codable {
|
|
let messages: [SnChatMessage]
|
|
let currentTimestamp: Date
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case messages
|
|
case currentTimestamp = "current_timestamp"
|
|
}
|
|
}
|