Remove dependency on AppNavigationStateService from DefaultUnifiedPushCurrentUserPushConfigProvider
This commit is contained in:
parent
705b1b08f2
commit
afdfe28ef4
21 changed files with 124 additions and 160 deletions
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.currentSessionId
|
||||
|
||||
interface UnifiedPushCurrentUserPushConfigProvider {
|
||||
suspend fun provide(): CurrentUserPushConfig?
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
) : UnifiedPushCurrentUserPushConfigProvider {
|
||||
override suspend fun provide(): CurrentUserPushConfig? {
|
||||
val currentSession = appNavigationStateService.appNavigationState.value.navigationState.currentSessionId() ?: return null
|
||||
val clientSecret = pushClientSecret.getSecretForUser(currentSession)
|
||||
val url = unifiedPushStore.getPushGateway(clientSecret) ?: return null
|
||||
val pushKey = unifiedPushStore.getEndpoint(clientSecret) ?: return null
|
||||
return CurrentUserPushConfig(
|
||||
url = url,
|
||||
pushKey = pushKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import dev.zacsweers.metro.ContributesIntoSet
|
|||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.Config
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
|
|
@ -25,7 +25,7 @@ class UnifiedPushProvider(
|
|||
private val unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
private val unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider,
|
||||
private val unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider,
|
||||
) : PushProvider {
|
||||
override val index = UnifiedPushConfig.INDEX
|
||||
override val name = UnifiedPushConfig.NAME
|
||||
|
|
@ -62,8 +62,8 @@ class UnifiedPushProvider(
|
|||
unRegisterUnifiedPushUseCase.cleanup(clientSecret)
|
||||
}
|
||||
|
||||
override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? {
|
||||
return unifiedPushCurrentUserPushConfigProvider.provide()
|
||||
override suspend fun getPushConfig(sessionId: SessionId): Config? {
|
||||
return unifiedPushSessionPushConfigProvider.provide(sessionId)
|
||||
}
|
||||
|
||||
override fun canRotateToken(): Boolean = false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.pushproviders.api.Config
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
|
||||
interface UnifiedPushSessionPushConfigProvider {
|
||||
suspend fun provide(sessionId: SessionId): Config?
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultUnifiedPushPushConfigProvider(
|
||||
private val pushClientSecret: PushClientSecret,
|
||||
private val unifiedPushStore: UnifiedPushStore,
|
||||
) : UnifiedPushSessionPushConfigProvider {
|
||||
override suspend fun provide(sessionId: SessionId): Config? {
|
||||
val clientSecret = pushClientSecret.getSecretForUser(sessionId)
|
||||
val url = unifiedPushStore.getPushGateway(clientSecret) ?: return null
|
||||
val pushKey = unifiedPushStore.getEndpoint(clientSecret) ?: return null
|
||||
return Config(
|
||||
url = url,
|
||||
pushKey = pushKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesIntoSet
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushApiFactory
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushCurrentUserPushConfigProvider
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushSessionPushConfigProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
|
|
@ -22,12 +23,13 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ContributesIntoSet(AppScope::class)
|
||||
@ContributesIntoSet(SessionScope::class)
|
||||
@Inject
|
||||
class UnifiedPushMatrixGatewayTest(
|
||||
private val sessionId: SessionId,
|
||||
private val unifiedPushApiFactory: UnifiedPushApiFactory,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider,
|
||||
private val unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 450
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
|
|
@ -44,7 +46,7 @@ class UnifiedPushMatrixGatewayTest(
|
|||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val config = unifiedPushCurrentUserPushConfigProvider.provide()
|
||||
val config = unifiedPushSessionPushConfigProvider.provide(sessionId)
|
||||
if (config == null) {
|
||||
delegate.updateState(
|
||||
description = "No current push provider",
|
||||
|
|
|
|||
|
|
@ -10,36 +10,16 @@ package io.element.android.libraries.pushproviders.unifiedpush
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_SECRET
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.Config
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.NavigationState
|
||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
|
||||
@Test
|
||||
fun `getCurrentUserPushConfig no session`() = runTest {
|
||||
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider()
|
||||
val result = sut.provide()
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCurrentUserPushConfig no push gateway`() = runTest {
|
||||
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
appNavigationStateService = FakeAppNavigationStateService(
|
||||
appNavigationState = MutableStateFlow(
|
||||
AppNavigationState(
|
||||
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
|
||||
isInForeground = true
|
||||
)
|
||||
)
|
||||
),
|
||||
pushClientSecret = FakePushClientSecret(
|
||||
getSecretForUserResult = { A_SECRET }
|
||||
),
|
||||
|
|
@ -47,21 +27,13 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
|
|||
getPushGatewayResult = { null }
|
||||
),
|
||||
)
|
||||
val result = sut.provide()
|
||||
val result = sut.provide(A_SESSION_ID)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCurrentUserPushConfig no push key`() = runTest {
|
||||
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
appNavigationStateService = FakeAppNavigationStateService(
|
||||
appNavigationState = MutableStateFlow(
|
||||
AppNavigationState(
|
||||
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
|
||||
isInForeground = true
|
||||
)
|
||||
)
|
||||
),
|
||||
pushClientSecret = FakePushClientSecret(
|
||||
getSecretForUserResult = { A_SECRET }
|
||||
),
|
||||
|
|
@ -70,21 +42,13 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
|
|||
getEndpointResult = { null }
|
||||
),
|
||||
)
|
||||
val result = sut.provide()
|
||||
val result = sut.provide(A_SESSION_ID)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCurrentUserPushConfig ok`() = runTest {
|
||||
val sut = createDefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
appNavigationStateService = FakeAppNavigationStateService(
|
||||
appNavigationState = MutableStateFlow(
|
||||
AppNavigationState(
|
||||
navigationState = NavigationState.Session(owner = "owner", sessionId = A_SESSION_ID),
|
||||
isInForeground = true
|
||||
)
|
||||
)
|
||||
),
|
||||
pushClientSecret = FakePushClientSecret(
|
||||
getSecretForUserResult = { A_SECRET }
|
||||
),
|
||||
|
|
@ -93,19 +57,17 @@ class DefaultUnifiedPushCurrentUserPushConfigProviderTest {
|
|||
getEndpointResult = { "aEndpoint" }
|
||||
),
|
||||
)
|
||||
val result = sut.provide()
|
||||
assertThat(result).isEqualTo(CurrentUserPushConfig("aPushGateway", "aEndpoint"))
|
||||
val result = sut.provide(A_SESSION_ID)
|
||||
assertThat(result).isEqualTo(Config("aPushGateway", "aEndpoint"))
|
||||
}
|
||||
|
||||
private fun createDefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
pushClientSecret: PushClientSecret = FakePushClientSecret(),
|
||||
unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(),
|
||||
appNavigationStateService: AppNavigationStateService = FakeAppNavigationStateService(),
|
||||
): DefaultUnifiedPushCurrentUserPushConfigProvider {
|
||||
return DefaultUnifiedPushCurrentUserPushConfigProvider(
|
||||
): DefaultUnifiedPushPushConfigProvider {
|
||||
return DefaultUnifiedPushPushConfigProvider(
|
||||
pushClientSecret = pushClientSecret,
|
||||
unifiedPushStore = unifiedPushStore,
|
||||
appNavigationStateService = appNavigationStateService,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import io.element.android.libraries.matrix.test.A_SECRET
|
|||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushCurrentUserPushConfigProvider
|
||||
import io.element.android.libraries.pushproviders.test.aSessionPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushDistributorProvider
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.troubleshoot.FakeUnifiedPushSessionPushConfigProvider
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
import io.element.android.libraries.pushstore.test.userpushstore.clientsecret.FakePushClientSecret
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
|
@ -211,13 +211,13 @@ class UnifiedPushProviderTest {
|
|||
|
||||
@Test
|
||||
fun `getCurrentUserPushConfig invokes the provider methods`() = runTest {
|
||||
val currentUserPushConfig = aCurrentUserPushConfig()
|
||||
val currentUserPushConfig = aSessionPushConfig()
|
||||
val unifiedPushProvider = createUnifiedPushProvider(
|
||||
unifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider(
|
||||
currentUserPushConfig = { currentUserPushConfig }
|
||||
unifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(
|
||||
config = { currentUserPushConfig }
|
||||
)
|
||||
)
|
||||
val result = unifiedPushProvider.getCurrentUserPushConfig()
|
||||
val result = unifiedPushProvider.getPushConfig(A_SESSION_ID)
|
||||
assertThat(result).isEqualTo(currentUserPushConfig)
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ class UnifiedPushProviderTest {
|
|||
unRegisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase = FakeUnregisterUnifiedPushUseCase(),
|
||||
pushClientSecret: PushClientSecret = FakePushClientSecret(),
|
||||
unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(),
|
||||
unifiedPushCurrentUserPushConfigProvider: UnifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider(),
|
||||
unifiedPushSessionPushConfigProvider: UnifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(),
|
||||
): UnifiedPushProvider {
|
||||
return UnifiedPushProvider(
|
||||
unifiedPushDistributorProvider = unifiedPushDistributorProvider,
|
||||
|
|
@ -256,7 +256,7 @@ class UnifiedPushProviderTest {
|
|||
unRegisterUnifiedPushUseCase = unRegisterUnifiedPushUseCase,
|
||||
pushClientSecret = pushClientSecret,
|
||||
unifiedPushStore = unifiedPushStore,
|
||||
unifiedPushCurrentUserPushConfigProvider = unifiedPushCurrentUserPushConfigProvider,
|
||||
unifiedPushSessionPushConfigProvider = unifiedPushSessionPushConfigProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushCurrentUserPushConfigProvider
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeUnifiedPushCurrentUserPushConfigProvider(
|
||||
private val currentUserPushConfig: () -> CurrentUserPushConfig? = { lambdaError() },
|
||||
) : UnifiedPushCurrentUserPushConfigProvider {
|
||||
override suspend fun provide(): CurrentUserPushConfig? {
|
||||
return currentUserPushConfig()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.pushproviders.api.Config
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushSessionPushConfigProvider
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakeUnifiedPushSessionPushConfigProvider(
|
||||
private val config: (SessionId) -> Config? = { lambdaError() },
|
||||
) : UnifiedPushSessionPushConfigProvider {
|
||||
override suspend fun provide(sessionId: SessionId): Config? {
|
||||
return config(sessionId)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,10 @@
|
|||
package io.element.android.libraries.pushproviders.unifiedpush.troubleshoot
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.test.aCurrentUserPushConfig
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.pushproviders.api.Config
|
||||
import io.element.android.libraries.pushproviders.test.aSessionPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.FakeUnifiedPushApiFactory
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.UnifiedPushConfig
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.invalidDiscoveryResponse
|
||||
|
|
@ -27,7 +29,7 @@ class UnifiedPushMatrixGatewayTestTest {
|
|||
@Test
|
||||
fun `test UnifiedPushMatrixGatewayTest success`() = runTest {
|
||||
val sut = createUnifiedPushMatrixGatewayTest(
|
||||
currentUserPushConfig = aCurrentUserPushConfig(),
|
||||
config = aSessionPushConfig(),
|
||||
discoveryResponse = matrixDiscoveryResponse,
|
||||
)
|
||||
sut.runAndTestState {
|
||||
|
|
@ -41,7 +43,7 @@ class UnifiedPushMatrixGatewayTestTest {
|
|||
@Test
|
||||
fun `test UnifiedPushMatrixGatewayTest no config found`() = runTest {
|
||||
val sut = createUnifiedPushMatrixGatewayTest(
|
||||
currentUserPushConfig = null,
|
||||
config = null,
|
||||
discoveryResponse = matrixDiscoveryResponse,
|
||||
)
|
||||
sut.runAndTestState {
|
||||
|
|
@ -55,7 +57,7 @@ class UnifiedPushMatrixGatewayTestTest {
|
|||
@Test
|
||||
fun `test UnifiedPushMatrixGatewayTest not valid gateway`() = runTest {
|
||||
val sut = createUnifiedPushMatrixGatewayTest(
|
||||
currentUserPushConfig = aCurrentUserPushConfig(),
|
||||
config = aSessionPushConfig(),
|
||||
discoveryResponse = invalidDiscoveryResponse,
|
||||
)
|
||||
sut.runAndTestState {
|
||||
|
|
@ -72,7 +74,7 @@ class UnifiedPushMatrixGatewayTestTest {
|
|||
@Test
|
||||
fun `test UnifiedPushMatrixGatewayTest network error`() = runTest {
|
||||
val sut = createUnifiedPushMatrixGatewayTest(
|
||||
currentUserPushConfig = aCurrentUserPushConfig(),
|
||||
config = aSessionPushConfig(),
|
||||
discoveryResponse = { error("Network error") },
|
||||
)
|
||||
sut.runAndTestState {
|
||||
|
|
@ -91,14 +93,16 @@ class UnifiedPushMatrixGatewayTestTest {
|
|||
}
|
||||
|
||||
private fun TestScope.createUnifiedPushMatrixGatewayTest(
|
||||
currentUserPushConfig: CurrentUserPushConfig? = null,
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
config: Config? = null,
|
||||
discoveryResponse: () -> DiscoveryResponse = matrixDiscoveryResponse,
|
||||
): UnifiedPushMatrixGatewayTest {
|
||||
return UnifiedPushMatrixGatewayTest(
|
||||
sessionId = sessionId,
|
||||
unifiedPushApiFactory = FakeUnifiedPushApiFactory(discoveryResponse),
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
unifiedPushCurrentUserPushConfigProvider = FakeUnifiedPushCurrentUserPushConfigProvider(
|
||||
currentUserPushConfig = { currentUserPushConfig }
|
||||
unifiedPushSessionPushConfigProvider = FakeUnifiedPushSessionPushConfigProvider(
|
||||
config = { config }
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue