ios: delete temp data from storage view (#7626)

* ios: delete temp data from storage view

The temp folder is the XFTP agent work directory and holds scratch
files for received-file encryption and roster transfers, so deleting it
under a running chat breaks those writes mid-flight. The button is only
enabled while chat is stopped, the same precondition core deleteStorage
relies on, and the row says how to stop chat.

* ios: show delete temp data alert over settings sheet

AlertManager presents from ContentView, which is behind the settings
sheet, so the confirmation never appeared. The alert now also warns
that interrupted transfers never complete and that videos in unsent
drafts, which live in the temp folder, are lost, and the caption names
the screen where chat is stopped.

* ios: say unsent draft videos are sent without the file

A draft video whose converted file was deleted from the temp folder is
still sent, as a preview with no file, so "lost" understated it.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
Narasimha-sc
2026-10-10 22:38:14 +01:00
committed by GitHub
co-authored by Evgeny Poberezkin
parent 7d8a174388
commit d457bc18d2
2 changed files with 51 additions and 8 deletions
@@ -10,33 +10,66 @@ import SwiftUI
import SimpleXChat
struct StorageView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@State var appGroupFiles: [String: Int64] = [:]
@State var documentsFiles: [String: Int64] = [:]
var body: some View {
ScrollView {
VStack(alignment: .leading) {
directoryView("App group:", appGroupFiles)
directoryView("App group:", getGroupContainerDirectory(), appGroupFiles)
if !documentsFiles.isEmpty {
directoryView("Documents:", documentsFiles)
directoryView("Documents:", getDocumentsDirectory(), documentsFiles)
}
}
}
.padding()
.onAppear {
appGroupFiles = traverseFiles(in: getGroupContainerDirectory())
documentsFiles = traverseFiles(in: getDocumentsDirectory())
}
.onAppear(perform: loadFiles)
}
private func loadFiles() {
appGroupFiles = traverseFiles(in: getGroupContainerDirectory())
documentsFiles = traverseFiles(in: getDocumentsDirectory())
}
@ViewBuilder
private func directoryView(_ name: LocalizedStringKey, _ contents: [String: Int64]) -> some View {
private func directoryView(_ name: LocalizedStringKey, _ dir: URL, _ contents: [String: Int64]) -> some View {
Text(name).font(.headline)
ForEach(Array(contents), id: \.key) { (key, value) in
Text(key).bold() + Text(verbatim: " ") + Text((ByteCountFormatter.string(fromByteCount: value, countStyle: .binary)))
let sizeText = Text(key).bold() + Text(verbatim: " ") + Text((ByteCountFormatter.string(fromByteCount: value, countStyle: .binary)))
if dir.appendingPathComponent(key).path == getTempFilesDirectory().path {
let stopped = m.chatRunning == false
HStack {
sizeText
Button("Delete temp data", role: .destructive, action: confirmDeleteTempFiles)
.disabled(!stopped)
}
if !stopped {
Text("Stop chat in Settings → Chat data → Database passphrase & export to delete temp data.")
.font(.footnote)
.foregroundColor(theme.colors.secondary)
}
} else {
sizeText
}
}
}
private func confirmDeleteTempFiles() {
showAlert(
NSLocalizedString("Delete temp data?", comment: "alert title"),
message: NSLocalizedString("Files being sent or received will never complete. Videos in unsent drafts will be sent without the file.", comment: "alert message"),
actions: {[
UIAlertAction(title: NSLocalizedString("Delete", comment: "alert action"), style: .destructive) { _ in
deleteTempFiles()
loadFiles()
},
cancelAlertAction
]}
)
}
private func traverseFiles(in dir: URL) -> [String: Int64] {
var res: [String: Int64] = [:]
let fm = FileManager.default
+10
View File
@@ -140,6 +140,16 @@ public func deleteAppFiles() {
}
}
public func deleteTempFiles() {
let fm = FileManager.default
do {
try fm.removeItem(at: getTempFilesDirectory())
try fm.createDirectory(at: getTempFilesDirectory(), withIntermediateDirectories: true)
} catch {
logger.error("FileUtils deleteTempFiles error: \(error.localizedDescription)")
}
}
public func fileSize(_ url: URL) -> Int? { // in bytes
do {
let val = try url.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey])