132 lines · 4.8 KB · Swift
RawDownload
import CodexBarCoreimport SwiftUI struct CodexResetCreditPresentationItem: Equatable {    let expiryText: String    let compactExpiryText: String} struct CodexResetCreditsPresentation: Equatable {    let text: String    let items: [CodexResetCreditPresentationItem]     var expirySummaryText: String {        let visibleItems = self.items.prefix(4).map(\.compactExpiryText)        let hiddenCount = self.items.count - visibleItems.count        let suffix = hiddenCount > 0 ? ["+\(hiddenCount)"] : []        return (visibleItems + suffix).joined(separator: " · ")    }     var helpText: String {        self.items.enumerated().map { index, item in            "\(index + 1). \(item.expiryText)"        }.joined(separator: "\n")    }     var accessibilityLabel: String {        [L("Limit Reset Credits"), self.text, self.helpText]            .filter { !$0.isEmpty }            .joined(separator: ", ")    }     static func make(        snapshot: CodexRateLimitResetCreditsSnapshot,        resetStyle: ResetTimeDisplayStyle,        now: Date) -> CodexResetCreditsPresentation?    {        let inventory = snapshot.availableInventory(at: now)        guard !inventory.credits.isEmpty else { return nil }        let items = inventory.credits.map { credit in            Self.presentationItem(for: credit, resetStyle: resetStyle, now: now)        }        return CodexResetCreditsPresentation(            text: Self.availableText(count: inventory.count),            items: items)    }     private static func availableText(count: Int) -> String {        count == 1 ? L("1 available") : String(format: L("%d available"), count)    }     private static func presentationItem(        for credit: CodexRateLimitResetCredit,        resetStyle: ResetTimeDisplayStyle,        now: Date) -> CodexResetCreditPresentationItem    {        guard let expiresAt = credit.expiresAt else {            return CodexResetCreditPresentationItem(expiryText: L("No expiry"), compactExpiryText: L("No expiry"))        }        let formattedTime = Self.formattedTime(expiresAt, resetStyle: resetStyle, now: now)        let compactExpiryText = resetStyle == .countdown && formattedTime.hasPrefix("in ")            ? String(formattedTime.dropFirst(3))            : formattedTime        return CodexResetCreditPresentationItem(            expiryText: String(format: L("Expires %@"), formattedTime),            compactExpiryText: compactExpiryText)    }     private static func formattedTime(        _ expiresAt: Date,        resetStyle: ResetTimeDisplayStyle,        now: Date) -> String    {        switch resetStyle {        case .absolute:            return UsageFormatter.resetDescription(from: expiresAt, now: now)        case .countdown:            let countdown = UsageFormatter.resetCountdownDescription(from: expiresAt, now: now)            return countdown == "now" ? L("now") : countdown        }    }} struct CodexResetCreditsContent: View {    let presentation: CodexResetCreditsPresentation    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 4) {            Text(L("Limit Reset Credits"))                .font(.body)                .fontWeight(.medium)                .lineLimit(1)            HStack(alignment: .firstTextBaseline, spacing: 8) {                Text(self.presentation.text)                    .font(.footnote.weight(.semibold))                    .foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))                    .lineLimit(1)                    .layoutPriority(1)                Spacer(minLength: 8)                HStack(alignment: .firstTextBaseline, spacing: 4) {                    Image(systemName: "clock")                        .font(.caption2)                    Text(self.presentation.expirySummaryText)                        .font(.caption)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .lineLimit(1)                        .minimumScaleFactor(0.8)                }                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                .accessibilityHidden(true)            }        }        .frame(maxWidth: .infinity, alignment: .leading)        .help(self.presentation.helpText)        .accessibilityElement(children: .combine)        .accessibilityLabel(self.presentation.accessibilityLabel)    }} extension UsageMenuCardView.Model {    static func codexResetCredits(input: Input) -> CodexResetCreditsPresentation? {        guard input.provider == .codex,              let resetCredits = input.snapshot?.codexResetCredits        else {            return nil        }        return CodexResetCreditsPresentation.make(            snapshot: resetCredits,            resetStyle: input.resetTimeDisplayStyle,            now: input.now)    }}