Display the store sizes under the developer options menu

This commit is contained in:
Stefan Ceriu
2025-12-18 17:49:39 +02:00
committed by Stefan Ceriu
parent 8a1d0fe8b1
commit 53a20f2a89
9 changed files with 128 additions and 8 deletions
@@ -252,7 +252,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
}
private func presentDeveloperOptions() {
let coordinator = DeveloperOptionsScreenCoordinator(appSettings: flowParameters.appSettings, appHooks: flowParameters.appHooks)
let coordinator = DeveloperOptionsScreenCoordinator(appSettings: flowParameters.appSettings,
appHooks: flowParameters.appHooks,
clientProxy: flowParameters.userSession.clientProxy)
coordinator.actions
.sink { [weak self] action in
@@ -150,5 +150,7 @@ extension ClientProxyMock {
underlyingHideInviteAvatarsPublisher = CurrentValueSubject<Bool, Never>(configuration.hideInviteAvatars).asCurrentValuePublisher()
underlyingMaxMediaUploadSize = .success(configuration.maxMediaUploadSize)
storeSizesReturnValue = .success(.init(cryptoStore: 1, stateStore: 9, eventCacheStore: 8, mediaStore: 6))
}
}
@@ -4707,6 +4707,70 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
return optimizeStoresReturnValue
}
}
//MARK: - storeSizes
var storeSizesUnderlyingCallsCount = 0
var storeSizesCallsCount: Int {
get {
if Thread.isMainThread {
return storeSizesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = storeSizesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
storeSizesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
storeSizesUnderlyingCallsCount = newValue
}
}
}
}
var storeSizesCalled: Bool {
return storeSizesCallsCount > 0
}
var storeSizesUnderlyingReturnValue: Result<StoreSizes, ClientProxyError>!
var storeSizesReturnValue: Result<StoreSizes, ClientProxyError>! {
get {
if Thread.isMainThread {
return storeSizesUnderlyingReturnValue
} else {
var returnValue: Result<StoreSizes, ClientProxyError>? = nil
DispatchQueue.main.sync {
returnValue = storeSizesUnderlyingReturnValue
}
return returnValue!
}
}
set {
if Thread.isMainThread {
storeSizesUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
storeSizesUnderlyingReturnValue = newValue
}
}
}
}
var storeSizesClosure: (() async -> Result<StoreSizes, ClientProxyError>)?
func storeSizes() async -> Result<StoreSizes, ClientProxyError> {
storeSizesCallsCount += 1
if let storeSizesClosure = storeSizesClosure {
return await storeSizesClosure()
} else {
return storeSizesReturnValue
}
}
//MARK: - fetchMediaPreviewConfiguration
var fetchMediaPreviewConfigurationUnderlyingCallsCount = 0
@@ -23,10 +23,11 @@ final class DeveloperOptionsScreenCoordinator: CoordinatorProtocol {
actionsSubject.eraseToAnyPublisher()
}
init(appSettings: AppSettings, appHooks: AppHooks) {
init(appSettings: AppSettings, appHooks: AppHooks, clientProxy: ClientProxyProtocol) {
viewModel = DeveloperOptionsScreenViewModel(developerOptions: appSettings,
elementCallBaseURL: appSettings.elementCallBaseURL,
appHooks: appHooks)
appHooks: appHooks,
clientProxy: clientProxy)
viewModel.actions
.sink { [weak self] action in
@@ -15,7 +15,17 @@ enum DeveloperOptionsScreenViewModelAction {
struct DeveloperOptionsScreenViewState: BindableState {
let elementCallBaseURL: URL
let appHooks: AppHooks
var storeSizes: [StoreSize]?
var bindings: DeveloperOptionsScreenViewStateBindings
struct StoreSize: Identifiable {
let name: String
let size: String
var id: String {
name + size
}
}
}
// periphery: ignore - subscripts are seen as false positive
@@ -18,11 +18,32 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
actionsSubject.eraseToAnyPublisher()
}
init(developerOptions: DeveloperOptionsProtocol, elementCallBaseURL: URL, appHooks: AppHooks) {
let bindings = DeveloperOptionsScreenViewStateBindings(developerOptions: developerOptions)
let state = DeveloperOptionsScreenViewState(elementCallBaseURL: elementCallBaseURL, appHooks: appHooks, bindings: bindings)
init(developerOptions: DeveloperOptionsProtocol, elementCallBaseURL: URL, appHooks: AppHooks, clientProxy: ClientProxyProtocol) {
super.init(initialViewState: .init(elementCallBaseURL: elementCallBaseURL,
appHooks: appHooks,
bindings: .init(developerOptions: developerOptions)))
super.init(initialViewState: state)
Task {
if case let .success(sizes) = await clientProxy.storeSizes() {
let formatter = ByteCountFormatStyle(style: .file)
var components = [DeveloperOptionsScreenViewState.StoreSize]()
if let cryptoStore = sizes.cryptoStore {
components.append(.init(name: "CryptoStore", size: formatter.format(Int64(cryptoStore))))
}
if let stateStore = sizes.stateStore {
components.append(.init(name: "StateStore", size: formatter.format(Int64(stateStore))))
}
if let eventCacheStore = sizes.eventCacheStore {
components.append(.init(name: "EventCacheStore", size: formatter.format(Int64(eventCacheStore))))
}
if let mediaStore = sizes.mediaStore {
components.append(.init(name: "MediaStore", size: formatter.format(Int64(mediaStore))))
}
state.storeSizes = components
}
}
}
override func process(viewAction: DeveloperOptionsScreenViewAction) {
@@ -21,6 +21,14 @@ struct DeveloperOptionsScreen: View {
var body: some View {
Form {
if let storeSizes = context.viewState.storeSizes {
Section("Usage") {
ForEach(storeSizes) { storeSize in
LabeledContent(storeSize.name, value: storeSize.size)
}
}
}
Section("Logging") {
LogLevelConfigurationView(logLevel: $context.logLevel)
@@ -207,7 +215,8 @@ private extension Set<TraceLogPack> {
struct DeveloperOptionsScreen_Previews: PreviewProvider {
static let viewModel = DeveloperOptionsScreenViewModel(developerOptions: ServiceLocator.shared.settings,
elementCallBaseURL: ServiceLocator.shared.settings.elementCallBaseURL,
appHooks: AppHooks())
appHooks: AppHooks(),
clientProxy: ClientProxyMock(.init()))
static var previews: some View {
NavigationStack {
DeveloperOptionsScreen(context: viewModel.context)
@@ -816,6 +816,15 @@ class ClientProxy: ClientProxyProtocol {
}
}
func storeSizes() async -> Result<StoreSizes, ClientProxyError> {
do {
return try await .success(client.getStoreSizes())
} catch {
MXLog.error("Failed optimizing client stores with error: \(error)")
return .failure(.sdkError(error))
}
}
func fetchMediaPreviewConfiguration() async -> Result<MediaPreviewConfig?, ClientProxyError> {
do {
let config = try await client.fetchMediaPreviewConfig()
@@ -217,6 +217,8 @@ protocol ClientProxyProtocol: AnyObject {
@discardableResult func optimizeStores() async -> Result<Void, ClientProxyError>
func storeSizes() async -> Result<StoreSizes, ClientProxyError>
func fetchMediaPreviewConfiguration() async -> Result<MediaPreviewConfig?, ClientProxyError>
// MARK: - Ignored users