Archived
fix: share private chat files and harden native lifecycle
This commit is contained in:
1 parent
7dcd06c911
commit
ec510b938b
8 files changed
+83
-15
No files matched your search
@@ -22,6 +22,7 @@ final class ChatStore: ObservableObject {
|
||||
@Published var researchAvailable = false
|
||||
@Published var research = false
|
||||
@Published var exportText: String?
|
||||
@Published var exportFile: PrivateChatExport?
|
||||
private var syncCursor = 0
|
||||
private var syncTask: Task<Void, Never>?
|
||||
@Published var model = ""
|
||||
@@ -41,7 +42,7 @@ final class ChatStore: ObservableObject {
|
||||
var activeRun: HopeRun? { runs.first(where: \.active) }
|
||||
|
||||
init(api: ChatTransport) { self.api = api; self.features = ChatFeatureStore(api: api) }
|
||||
deinit { stream?.cancel() }
|
||||
deinit { stream?.cancel(); syncTask?.cancel() }
|
||||
private func call<T: Decodable>(_ path: String, method: String = "GET", body: [String: Any]? = nil, query: [URLQueryItem] = []) async throws -> T {
|
||||
let epoch = generation
|
||||
let data = try await api.request(path, method: method, body: body, query: query)
|
||||
@@ -77,7 +78,7 @@ final class ChatStore: ObservableObject {
|
||||
func open(_ conversation: HopeConversation) async {
|
||||
if let selected { drafts[selected.id] = draft }
|
||||
generation += 1; stream?.cancel(); stream = nil
|
||||
selected = conversation; messages = []; runs = []; progress = nil; approval = nil
|
||||
selected = conversation; messages = []; runs = []; progress = nil; approval = nil; exportText = nil; exportFile = nil
|
||||
draft = drafts[conversation.id] ?? ""; pendingKey = nil; selectedAttachmentIds = []; olderMessagesCursor = nil; error = nil
|
||||
do {
|
||||
let detail: ConversationDetail = try await call("api/conversations/\(conversation.id)")
|
||||
@@ -249,16 +250,20 @@ final class ChatStore: ObservableObject {
|
||||
let bytes = try await api.request("api/conversations/\(selected.id)/export", method: "GET", body: nil, query: [URLQueryItem(name: "format", value: format)])
|
||||
guard epoch == generation, !closed else { return }
|
||||
exportText = String(data: bytes, encoding: .utf8)
|
||||
exportFile = PrivateChatExport(data: bytes, filename: "Hope-chat-\(selected.id).\(format == "json" ? "json" : "md")", type: format == "json" ? .json : .plainText)
|
||||
} catch { await failure(error) }
|
||||
}
|
||||
func foreground(_ active: Bool) {
|
||||
syncTask?.cancel(); syncTask = nil
|
||||
guard active, !closed else { return }
|
||||
guard active, !closed else { stream?.cancel(); stream = nil; return }
|
||||
resume()
|
||||
syncTask = Task { [weak self] in
|
||||
guard let self else { return }
|
||||
await loadResearch()
|
||||
while !Task.isCancelled, !closed {
|
||||
await synchronize()
|
||||
await self?.loadResearch()
|
||||
while !Task.isCancelled {
|
||||
guard self?.closed == false else { return }
|
||||
await self?.synchronize()
|
||||
// Release the store before suspension; its retained task must not
|
||||
// keep an otherwise-discarded account alive indefinitely.
|
||||
do { try await Task.sleep(for: .seconds(5)) } catch { return }
|
||||
}
|
||||
}
|
||||
@@ -296,7 +301,7 @@ final class ChatStore: ObservableObject {
|
||||
} catch { if case ChatFailure.status(let status) = error, status == 401 || status == 403 { await failure(error) } }
|
||||
}
|
||||
/// Invalidate every outstanding callback before clearing account-visible content.
|
||||
func close() { syncTask?.cancel(); syncTask = nil; exportText = nil; research = false; features.close(); selectedAttachmentIds = []; closed = true; generation += 1; stream?.cancel(); stream = nil; api.cancel(); conversations = []; workspaces = []; selected = nil; messages = []; runs = []; draft = ""; drafts = [:]; cursors = [:]; pendingKey = nil; approval = nil; progress = nil; error = nil }
|
||||
func close() { syncTask?.cancel(); syncTask = nil; exportText = nil; exportFile = nil; research = false; features.close(); selectedAttachmentIds = []; closed = true; generation += 1; stream?.cancel(); stream = nil; api.cancel(); conversations = []; workspaces = []; selected = nil; messages = []; runs = []; draft = ""; drafts = [:]; cursors = [:]; pendingKey = nil; approval = nil; progress = nil; error = nil }
|
||||
}
|
||||
struct ChatModelChoice: Decodable, Identifiable { let id: String; let thinkingLevels: [String]? }
|
||||
struct ChatSettingsValue: Decodable { let model: String; let thinkingLevel: String }
|
||||
|
||||
Reference in new issue
Block a user