Delete history, allow retry
This commit is contained in:
114
Sesame/History/HistoryItem.swift
Normal file
114
Sesame/History/HistoryItem.swift
Normal file
@ -0,0 +1,114 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
struct HistoryItem {
|
||||
|
||||
/// The sent/received date (local time, not including compensation offset)
|
||||
let requestDate: Date
|
||||
|
||||
let request: Message.Content
|
||||
|
||||
let usedLocalConnection: Bool
|
||||
|
||||
var response: ClientState
|
||||
|
||||
let responseMessage: Message.Content?
|
||||
|
||||
let responseDate: Date
|
||||
|
||||
init(sent message: Message.Content, sentDate: Date, local: Bool, response: ClientState, responseDate: Date, responseMessage: Message.Content?) {
|
||||
self.requestDate = sentDate
|
||||
self.request = message
|
||||
self.responseMessage = responseMessage
|
||||
self.response = response
|
||||
self.responseDate = responseDate
|
||||
self.usedLocalConnection = local
|
||||
}
|
||||
|
||||
// MARK: Statistics
|
||||
|
||||
var roundTripTime: TimeInterval {
|
||||
responseDate.timeIntervalSince(requestDate)
|
||||
}
|
||||
|
||||
var deviceTime: Date? {
|
||||
guard let timestamp = responseMessage?.time else {
|
||||
return nil
|
||||
}
|
||||
return Date(timestamp: timestamp)
|
||||
}
|
||||
|
||||
var requestLatency: TimeInterval? {
|
||||
deviceTime?.timeIntervalSince(requestDate)
|
||||
}
|
||||
|
||||
var responseLatency: TimeInterval? {
|
||||
guard let deviceTime = deviceTime else {
|
||||
return nil
|
||||
}
|
||||
return responseDate.timeIntervalSince(deviceTime)
|
||||
}
|
||||
|
||||
var clockOffset: Int? {
|
||||
guard let deviceTime = deviceTime else {
|
||||
return nil
|
||||
}
|
||||
let estimatedArrival = requestDate.advanced(by: roundTripTime / 2)
|
||||
return Int(deviceTime.timeIntervalSince(estimatedArrival))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HistoryItem: Codable {
|
||||
|
||||
enum CodingKeys: Int, CodingKey {
|
||||
case requestDate = 1
|
||||
case request = 2
|
||||
case usedLocalConnection = 3
|
||||
case response = 4
|
||||
case responseMessage = 5
|
||||
case responseDate = 6
|
||||
}
|
||||
}
|
||||
|
||||
extension ClientState: Codable {
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let code = try decoder.singleValueContainer().decode(UInt8.self)
|
||||
self.init(code: code)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(code)
|
||||
}
|
||||
}
|
||||
|
||||
extension HistoryItem: Identifiable {
|
||||
|
||||
var id: UInt32 {
|
||||
requestDate.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
extension HistoryItem: Comparable {
|
||||
|
||||
static func < (lhs: HistoryItem, rhs: HistoryItem) -> Bool {
|
||||
lhs.requestDate < rhs.requestDate
|
||||
}
|
||||
}
|
||||
|
||||
extension HistoryItem {
|
||||
|
||||
static var mock: HistoryItem {
|
||||
let content = Message.Content(time: Date.now.timestamp, id: 123, device: 0)
|
||||
let content2 = Message.Content(time: (Date.now + 1).timestamp, id: 124, device: 0)
|
||||
return .init(
|
||||
sent: content,
|
||||
sentDate: .now,
|
||||
local: false,
|
||||
response: .openSesame,
|
||||
responseDate: .now + 2,
|
||||
responseMessage: content2)
|
||||
}
|
||||
}
|
72
Sesame/History/HistoryListItem.swift
Normal file
72
Sesame/History/HistoryListItem.swift
Normal file
@ -0,0 +1,72 @@
|
||||
import SwiftUI
|
||||
import SFSafeSymbols
|
||||
|
||||
private let df: DateFormatter = {
|
||||
let df = DateFormatter()
|
||||
df.dateStyle = .short
|
||||
df.timeStyle = .short
|
||||
return df
|
||||
}()
|
||||
|
||||
struct HistoryListItem: View {
|
||||
|
||||
let entry: HistoryItem
|
||||
|
||||
var entryTime: String {
|
||||
df.string(from: entry.requestDate)
|
||||
}
|
||||
|
||||
var roundTripText: String {
|
||||
"\(Int(entry.roundTripTime * 1000)) ms"
|
||||
}
|
||||
|
||||
var counterText: String {
|
||||
let sentCounter = entry.request.id
|
||||
let startText = "\(sentCounter)"
|
||||
guard let rCounter = entry.responseMessage?.id else {
|
||||
return startText
|
||||
}
|
||||
let diff = Int(rCounter) - Int(sentCounter)
|
||||
guard diff != 1 && diff != 0 else {
|
||||
return startText
|
||||
}
|
||||
return startText + " (\(diff))"
|
||||
}
|
||||
|
||||
var timeOffsetText: String? {
|
||||
guard let offset = entry.clockOffset else {
|
||||
return nil
|
||||
}
|
||||
return "\(offset) s"
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text(entry.response.description)
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Text(entryTime)
|
||||
}.padding(.bottom, 1)
|
||||
HStack {
|
||||
Image(systemSymbol: entry.usedLocalConnection ? .wifi : .network)
|
||||
Text(roundTripText)
|
||||
.font(.subheadline)
|
||||
Image(systemSymbol: .personalhotspot)
|
||||
Text(counterText)
|
||||
.font(.subheadline)
|
||||
if let timeOffsetText {
|
||||
Image(systemSymbol: .stopwatch)
|
||||
Text(timeOffsetText)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HistoryListItem_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HistoryListItem(entry: .mock)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user