jsdw_ios/QuickLocation/Model/PigeonModel.swift

327 lines
10 KiB
Swift

//
// PigeonModel.swift
// QuickLocation
//
import ObjectMapper
import UIKit
struct PigeonLocalTemplate: Equatable {
let id: String
let title: String
let mediaType: String
let coverAssetName: String
let contentAssetName: String
static let imageTemplates: [PigeonLocalTemplate] = [
PigeonLocalTemplate(
id: "come_play",
title: "出来玩",
mediaType: "image",
coverAssetName: "PigeonMessage/template_come_play_cover",
contentAssetName: "PigeonMessage/template_come_play"
),
PigeonLocalTemplate(
id: "miss_you",
title: "想你了",
mediaType: "image",
coverAssetName: "PigeonMessage/template_miss_you_cover",
contentAssetName: "PigeonMessage/template_miss_you"
),
PigeonLocalTemplate(
id: "buy_for_me",
title: "给我买",
mediaType: "image",
coverAssetName: "PigeonMessage/template_buy_for_me_cover",
contentAssetName: "PigeonMessage/template_buy_for_me"
),
PigeonLocalTemplate(
id: "dislike_you",
title: "讨厌你",
mediaType: "image",
coverAssetName: "PigeonMessage/template_dislike_you_cover",
contentAssetName: "PigeonMessage/template_dislike_you"
),
PigeonLocalTemplate(
id: "who_upset_me",
title: "谁惹我",
mediaType: "image",
coverAssetName: "PigeonMessage/template_who_upset_me_cover",
contentAssetName: "PigeonMessage/template_who_upset_me"
),
PigeonLocalTemplate(
id: "apology",
title: "道歉",
mediaType: "image",
coverAssetName: "PigeonMessage/template_apology_cover",
contentAssetName: "PigeonMessage/template_apology"
),
PigeonLocalTemplate(
id: "arriving_soon",
title: "马上到",
mediaType: "image",
coverAssetName: "PigeonMessage/template_arriving_soon_cover",
contentAssetName: "PigeonMessage/template_arriving_soon"
),
PigeonLocalTemplate(
id: "proud",
title: "骄傲",
mediaType: "image",
coverAssetName: "PigeonMessage/template_proud_cover",
contentAssetName: "PigeonMessage/template_proud"
)
]
var coverImage: UIImage? {
UIImage(named: coverAssetName)
}
var contentImage: UIImage? {
UIImage(named: contentAssetName)
}
static func resolve(templateId: String) -> PigeonLocalTemplate? {
imageTemplates.first { $0.id == templateId }
}
}
struct PigeonVoiceTemplate: Equatable {
let id: String
let title: String
let duration: TimeInterval
static let templates: [PigeonVoiceTemplate] = [
PigeonVoiceTemplate(id: "bai_bai_hao_ha", title: "表白好哈", duration: 10),
PigeonVoiceTemplate(id: "bai_bai_hao_ha_2", title: "表白好哈", duration: 10),
PigeonVoiceTemplate(id: "bai_bai_hao_ha_3", title: "表白好哈", duration: 10)
]
static func resolve(templateId: String) -> PigeonVoiceTemplate? {
templates.first { $0.id == templateId }
}
}
struct PigeonTemplateExtra: Mappable {
var media_type: String = ""
var template_id: String = ""
init?(map: Map) {}
static func decode(_ value: Any?) -> PigeonTemplateExtra? {
if let extra = value as? PigeonTemplateExtra {
return extra
}
if let json = value as? [String: Any] {
return PigeonTemplateExtra(JSON: json)
}
let data: Data?
if let value = value as? String {
data = value.data(using: .utf8)
} else {
data = value as? Data
}
guard let data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return nil
}
return PigeonTemplateExtra(JSON: json)
}
mutating func mapping(map: Map) {
media_type <- map["media_type"]
template_id <- map["template_id"]
// Read legacy payloads, but always send the canonical two-field shape.
if template_id.isEmpty {
var legacyImageID = ""
legacyImageID <- map["image_template_id"]
if !legacyImageID.isEmpty {
template_id = legacyImageID
media_type = "image"
} else {
var legacyVoiceID = ""
legacyVoiceID <- map["voice_template_id"]
if !legacyVoiceID.isEmpty {
template_id = legacyVoiceID
media_type = "voice"
}
}
} else if media_type.isEmpty,
PigeonLocalTemplate.resolve(templateId: template_id) != nil {
media_type = "image"
}
}
var normalizedMediaType: String {
media_type.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
}
var normalizedTemplateID: String {
template_id.trimmingCharacters(in: .whitespacesAndNewlines)
}
var sendParameters: [String: Any]? {
let mediaType = normalizedMediaType
let templateID = normalizedTemplateID
guard !mediaType.isEmpty, !templateID.isEmpty else { return nil }
return ["media_type": mediaType, "template_id": templateID]
}
}
private let kPigeonTemplateExtraTransform = TransformOf<PigeonTemplateExtra, Any>(fromJSON: { value in
PigeonTemplateExtra.decode(value)
}, toJSON: { value in
value?.sendParameters
})
struct PigeonSentResponse: BaseModelProtocol {
var code: String?
var message: String?
var list: [PigeonSentMessage] = []
init?(map: Map) {}
mutating func mapping(map: Map) {
code <- (map["code"], kIntTransformStr)
message <- map["message"]
list <- map["data"]
}
}
struct PigeonSentMessage: Mappable {
var message_uuid: String = ""
var group_key: String = ""
var from_user: PigeonSentUser?
var to_user: [PigeonSentUser] = []
var msg_type: Int = 0
var msg: String = ""
var text_msg: String = ""
var bg_img: String = ""
var extra: PigeonTemplateExtra?
var expire_time: Int64 = 0
var send_time: Int64 = 0
init?(map: Map) {}
mutating func mapping(map: Map) {
message_uuid <- map["message_uuid"]
group_key <- map["group_key"]
from_user <- map["from_user"]
to_user <- map["to_user"]
msg_type <- map["msg_type"]
msg <- map["msg"]
text_msg <- map["text_msg"]
bg_img <- map["bg_img"]
extra <- (map["extra"], kPigeonTemplateExtraTransform)
expire_time <- map["expire_time"]
send_time <- map["send_time"]
}
var isVoice: Bool { msg_type == 2 }
var localTemplate: PigeonLocalTemplate? {
guard extra?.normalizedMediaType == "image" else { return nil }
let templateId = extra?.normalizedTemplateID ?? ""
return PigeonLocalTemplate.resolve(templateId: templateId)
}
var localTemplateImage: UIImage? {
localTemplate?.contentImage
}
var captionText: String {
if isVoice { return "" }
let fromMsg = msg.trimmingCharacters(in: .whitespacesAndNewlines)
if !fromMsg.isEmpty, !fromMsg.lowercased().hasPrefix("http") {
return fromMsg
}
return text_msg
}
var mediaURL: URL? {
let value = (isVoice ? msg : bg_img).trimmingCharacters(in: .whitespacesAndNewlines)
let fallback = msg.trimmingCharacters(in: .whitespacesAndNewlines)
let raw = value.lowercased().hasPrefix("http") ? value : fallback
guard raw.lowercased().hasPrefix("http"), let url = URL(string: raw) else {
return nil
}
return url
}
func toHistoryItem() -> PigeonHistoryItem {
PigeonHistoryItem(
id: message_uuid.isEmpty ? UUID().uuidString : message_uuid,
dateText: Self.dateText(from: send_time),
timeText: Self.timeText(from: send_time),
kind: isVoice ? .voice : .image,
caption: captionText,
image: localTemplateImage,
mediaURL: mediaURL,
senderAvatar: from_user?.avatarImage,
receiverAvatars: to_user.map(\.avatarImage),
duration: 0
)
}
private static func date(from timestamp: Int64) -> Date {
Date(timeIntervalSince1970: TimeInterval(timestamp))
}
private static func dateText(from timestamp: Int64) -> String {
let date = date(from: timestamp)
let calendar = Calendar.current
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
let weekday = weekdays[calendar.component(.weekday, from: date) - 1]
return "\(month).\(day) \(weekday)"
}
private static func timeText(from timestamp: Int64) -> String {
let date = date(from: timestamp)
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)
let period = hour < 12 ? "上午" : "下午"
let hour12 = hour % 12 == 0 ? 12 : hour % 12
return String(format: "%@ %d:%02d", period, hour12, minute)
}
}
struct PigeonSentUser: Mappable {
var user_id: String = ""
var nick_name: String = ""
var remark: String = ""
var head_pic: String = ""
var extra = MemberExtra()
init?(map: Map) {}
mutating func mapping(map: Map) {
user_id <- map["user_id"]
nick_name <- map["nick_name"]
remark <- map["remark"]
head_pic <- map["head_pic"]
extra <- (map["extra"], kMemberExtraTransform)
if extra.relation_idx.isEmpty {
extra.relation_idx <- (map["relation_idx"], kMemberRelationIndexTransform)
}
}
var displayName: String {
let remarkName = remark.trimmingCharacters(in: .whitespacesAndNewlines)
if !remarkName.isEmpty { return remarkName }
return nick_name
}
var avatarImage: UIImage {
let iconName = head_pic.trimmingCharacters(in: .whitespacesAndNewlines)
guard !iconName.isEmpty,
let image = UIImage(named: "UserIcon/\(iconName)") else {
return UIImage(named: "UserIcon/1") ?? UIImage()
}
return image
}
}