Add MailRootView and related components for mail functionality
- Implement MailRootView with navigation and sidebar for mail management. - Create MailSidebarView, ThreadListView, and ThreadDetailView for displaying mail content. - Introduce ComposeView for composing new messages. - Add MailTheme for consistent styling across mail components. - Implement adaptive layouts for iOS and macOS. - Create unit tests for AppNavigationCommand and AppViewModel to ensure correct functionality.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import Foundation
|
||||
|
||||
protocol AppControlServicing {
|
||||
func commands() -> AsyncStream<AppNavigationCommand>
|
||||
}
|
||||
|
||||
struct MockBackendControlService: AppControlServicing {
|
||||
static let controlFileEnvironmentKey = "SOCIALIO_CONTROL_FILE"
|
||||
static let pollingIntervalEnvironmentKey = "SOCIALIO_CONTROL_POLL_MS"
|
||||
|
||||
private let environment: [String: String]
|
||||
|
||||
init(environment: [String: String] = ProcessInfo.processInfo.environment) {
|
||||
self.environment = environment
|
||||
}
|
||||
|
||||
func commands() -> AsyncStream<AppNavigationCommand> {
|
||||
guard let controlFilePath = environment[Self.controlFileEnvironmentKey]?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!controlFilePath.isEmpty else {
|
||||
return AsyncStream { continuation in
|
||||
continuation.finish()
|
||||
}
|
||||
}
|
||||
|
||||
let controlFileURL = URL(fileURLWithPath: controlFilePath)
|
||||
let pollingInterval = pollingIntervalDuration
|
||||
|
||||
return AsyncStream { continuation in
|
||||
let task = Task.detached(priority: .background) {
|
||||
var lastAppliedPayload: String?
|
||||
|
||||
while !Task.isCancelled {
|
||||
if let payload = try? String(contentsOf: controlFileURL, encoding: .utf8) {
|
||||
let trimmedPayload = payload.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if !trimmedPayload.isEmpty,
|
||||
trimmedPayload != lastAppliedPayload,
|
||||
let command = AppNavigationCommand.parse(trimmedPayload) {
|
||||
lastAppliedPayload = trimmedPayload
|
||||
continuation.yield(command)
|
||||
}
|
||||
}
|
||||
|
||||
try? await Task.sleep(for: pollingInterval)
|
||||
}
|
||||
|
||||
continuation.finish()
|
||||
}
|
||||
|
||||
continuation.onTermination = { _ in
|
||||
task.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var pollingIntervalDuration: Duration {
|
||||
guard let rawValue = environment[Self.pollingIntervalEnvironmentKey],
|
||||
let milliseconds = Int(rawValue),
|
||||
milliseconds > 0 else {
|
||||
return .milliseconds(600)
|
||||
}
|
||||
|
||||
return .milliseconds(milliseconds)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user