[settingsSync] move actionRequired to SettingsSyncAuthService and make it a per-user action

GitOrigin-RevId: db929fe779b743474b8a91ee6954529d9a62c355
This commit is contained in:
Sergey Pak
2025-06-04 23:41:11 +02:00
committed by intellij-monorepo-bot
parent 58fb8ece5b
commit d5b6b431ac
7 changed files with 60 additions and 52 deletions

View File

@@ -3,6 +3,7 @@ package com.intellij.settingsSync.core
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger
import com.intellij.settingsSync.core.communicator.RemoteCommunicatorHolder
import com.intellij.util.EventDispatcher
import org.jetbrains.annotations.Nls
import java.awt.Component
@@ -16,7 +17,11 @@ class SettingsSyncStatusTracker {
private val eventDispatcher = EventDispatcher.create(Listener::class.java)
val currentStatus: SyncStatus
get() = state
get() {
if (RemoteCommunicatorHolder.isPendingAction())
return SyncStatus.ActionRequired
return state
}
init {
SettingsSyncEvents.getInstance().addListener(object: SettingsSyncEventListener {
@@ -54,19 +59,6 @@ class SettingsSyncStatusTracker {
eventDispatcher.multicaster.syncStatusChanged()
}
fun setActionRequired(message: @Nls String, actionTitle: @Nls String, action: suspend (Component?) -> Unit) {
lastSyncTime = -1
state = SyncStatus.ActionRequired(message, actionTitle, action)
eventDispatcher.multicaster.syncStatusChanged()
}
fun clearActionRequired() {
if (state is SyncStatus.ActionRequired) {
state = SyncStatus.Success
eventDispatcher.multicaster.syncStatusChanged()
}
}
fun isSyncSuccessful() = state == SyncStatus.Success
fun getLastSyncTime() = lastSyncTime
@@ -87,15 +79,6 @@ class SettingsSyncStatusTracker {
object Success: SyncStatus()
class Error(val errorMessage: @Nls String): SyncStatus()
/**
* @param message - text message that will be shown in the configurable label
* @param actionTitle - text to use in the button
* @param action - action to perform when clicked the button. The action will be performed under EDT
*/
class ActionRequired(val message: @Nls String,
val actionTitle: @Nls String,
private val action: suspend(Component?) -> Unit): SyncStatus() {
suspend fun execute(component: Component?) = action(component)
}
object ActionRequired : SyncStatus()
}
}

View File

@@ -2,6 +2,7 @@ package com.intellij.settingsSync.core.auth
import com.intellij.settingsSync.core.SettingsSyncStatusTracker
import com.intellij.settingsSync.core.communicator.SettingsSyncUserData
import org.jetbrains.annotations.Nls
import java.awt.Component
import javax.swing.Icon
@@ -27,13 +28,13 @@ interface SettingsSyncAuthService {
* The method must call `SettingsSyncEvents.getInstance().fireLoginStateChanged()` in order to propagate the changed state.
* If function is null, logout link in the UI is not visible
*/
val logoutFunction: ( suspend (Component?) -> Unit)?
val logoutFunction: (suspend (Component?) -> Unit)?
get() = null
/**
* Starts the login procedure (if necessary) and returns the Deferred of the logged-in user
*/
suspend fun login(parentComponent: Component?) : SettingsSyncUserData?
suspend fun login(parentComponent: Component?): SettingsSyncUserData?
/**
* Data of the current user. If there's no user, return null
@@ -48,5 +49,16 @@ interface SettingsSyncAuthService {
*/
fun crossSyncSupported(): Boolean = true
fun getPendingUserAction(userId:String): SettingsSyncStatusTracker.SyncStatus.ActionRequired? = null
fun getPendingUserAction(userId: String): PendingUserAction? = null
/**
* @param message - text message that will be shown in the configurable label
* @param actionTitle - text to use in the button
* @param action - action to perform when clicked the button. The action will be performed under EDT
*/
data class PendingUserAction(
val message: @Nls String,
val actionTitle: @Nls String,
val action: suspend (Component?) -> Unit
)
}

View File

@@ -91,6 +91,13 @@ object RemoteCommunicatorHolder : SettingsSyncEventListener {
}
}
fun isPendingAction(): Boolean {
if (!SettingsSyncSettings.getInstance().syncEnabled)
return false
val userId = SettingsSyncLocalSettings.getInstance().userId ?: return false
return getCurrentProvider()?.authService?.getPendingUserAction(userId) != null
}
fun createRemoteCommunicator(provider: SettingsSyncCommunicatorProvider,
userId: String,
parentDisposable: Disposable? = null): SettingsSyncRemoteCommunicator? {

View File

@@ -33,6 +33,7 @@ import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.settingsSync.core.*
import com.intellij.settingsSync.core.SettingsSyncBundle.message
import com.intellij.settingsSync.core.UpdateResult.*
import com.intellij.settingsSync.core.auth.SettingsSyncAuthService.PendingUserAction
import com.intellij.settingsSync.core.communicator.RemoteCommunicatorHolder
import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider
import com.intellij.settingsSync.core.communicator.SettingsSyncUserData
@@ -302,9 +303,6 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco
private fun handleDisableSync() {
SettingsSyncSettings.getInstance().syncEnabled = false
if (SettingsSyncStatusTracker.getInstance().currentStatus is SettingsSyncStatusTracker.SyncStatus.ActionRequired) {
SettingsSyncStatusTracker.getInstance().clearActionRequired()
}
when (disableSyncOption.get()) {
DisableSyncType.DISABLE_AND_REMOVE_DATA -> {
disableAndRemoveData()
@@ -325,14 +323,9 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco
private fun enableButtonAction(){
// enableCheckbox state here has already changed, so we react to it
if (enableCheckbox.isSelected) {
if (SettingsSyncStatusTracker.getInstance().currentStatus is SettingsSyncStatusTracker.SyncStatus.ActionRequired) {
val actionRequired = SettingsSyncStatusTracker.getInstance().currentStatus as SettingsSyncStatusTracker.SyncStatus.ActionRequired
MessagesService.getInstance().showMessageDialog(
null, null, actionRequired.message , message("status.action.settings.sync.pending.action"),
arrayOf(Messages.getOkButton()),
1, -1, Messages.getInformationIcon(), null, false, null
)
enableCheckbox.isSelected = false
val pendingUserAction = getPendingUserAction()
if (pendingUserAction != null) {
refreshActionRequired()
return
}
runWithModalProgressBlocking(ModalTaskOwner.component(configPanel), message("enable.sync.check.server.data.progress")) {
@@ -664,20 +657,19 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco
}
private fun refreshActionRequired() {
val currentStatus = SettingsSyncStatusTracker.getInstance().currentStatus
val userActionRequired: SettingsSyncStatusTracker.SyncStatus.ActionRequired? = userDropDownLink.selectedItem?.let {
RemoteCommunicatorHolder.getProvider(it.providerCode)?.authService?.getPendingUserAction(it.userId)
}
actionRequired.set(currentStatus is SettingsSyncStatusTracker.SyncStatus.ActionRequired || userActionRequired != null)
if (actionRequired.get()) {
val action = userActionRequired ?: currentStatus as? SettingsSyncStatusTracker.SyncStatus.ActionRequired ?: return
val userActionRequired: PendingUserAction? = getPendingUserAction()
actionRequired.set(userActionRequired != null)
if (userActionRequired != null) {
actionRequiredAction = {
action.execute(syncConfigPanel)
userActionRequired.action(syncConfigPanel)
refreshActionRequired()
if (!SettingsSyncSettings.getInstance().syncEnabled) {
enableButtonAction()
}
}
actionRequiredLabel.text = action.message
actionRequiredButton.text = action.actionTitle
cellDropDownLink.comment?.text = message("sync.status.action.required.comment", action.actionTitle, action.message)
actionRequiredLabel.text = userActionRequired.message
actionRequiredButton.text = userActionRequired.actionTitle
cellDropDownLink.comment?.text = message("sync.status.action.required.comment", userActionRequired.actionTitle, userActionRequired.message)
}
else {
cellDropDownLink.comment?.text = ""
@@ -687,6 +679,14 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco
}
}
private fun getPendingUserAction(): PendingUserAction? {
if (!enableCheckbox.isSelected)
return null
return userDropDownLink.selectedItem?.let {
RemoteCommunicatorHolder.getProvider(it.providerCode)?.authService?.getPendingUserAction(it.userId)
}
}
// triggers fake action, which causes SettingEditor to update and check if configurable was modified
// must be called on EDT
private fun triggerUpdateConfigurable() {

View File

@@ -8,6 +8,7 @@ import com.intellij.settingsSync.core.SettingsSyncBundle
import com.intellij.settingsSync.core.SettingsSyncEventListener
import com.intellij.settingsSync.core.SettingsSyncEvents
import com.intellij.settingsSync.core.SettingsSyncStatusTracker
import com.intellij.settingsSync.core.auth.SettingsSyncAuthService
import com.intellij.settingsSync.jba.auth.JBAAuthService
import com.intellij.util.io.HttpRequests
import com.jetbrains.cloudconfig.CloudConfigFileClientV2
@@ -171,12 +172,12 @@ internal open class CloudConfigServerCommunicator(private val serverUrl: String?
private fun setAuthActionRequired() {
if (SettingsSyncStatusTracker.getInstance().currentStatus is SettingsSyncStatusTracker.SyncStatus.ActionRequired)
return
SettingsSyncStatusTracker.getInstance().setActionRequired(
jbaAuthService.authRequiredAction = SettingsSyncAuthService.PendingUserAction(
SettingsSyncJbaBundle.message("action.settingsSync.authRequired"),
SettingsSyncBundle.message("config.button.login")) {
val userData = jbaAuthService.login(it)
if (userData != null) {
SettingsSyncStatusTracker.getInstance().clearActionRequired()
jbaAuthService.authRequiredAction = null
SettingsSyncStatusTracker.getInstance().updateOnSuccess()
}
}

View File

@@ -245,6 +245,10 @@ internal class JBAAuthService(private val cs: CoroutineScope) : SettingsSyncAuth
return null
}
internal var authRequiredAction: SettingsSyncAuthService.PendingUserAction? = null
override fun getPendingUserAction(userId: String): SettingsSyncAuthService.PendingUserAction? = authRequiredAction
}
private class LogInProgressDialog(parent: JComponent) : DialogWrapper(parent, false) {

View File

@@ -2,6 +2,7 @@ package com.intellij.settingsSync
import com.intellij.idea.TestFor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.settingsSync.core.SettingsSyncLocalSettings
import com.intellij.settingsSync.core.SettingsSyncMain
import com.intellij.settingsSync.core.SettingsSyncSettings
import com.intellij.settingsSync.core.SettingsSyncStatusTracker
@@ -136,7 +137,7 @@ internal class SettingsSyncAuthTest() : BasePlatformTestCase() {
communicator.checkServerState()
//assertFalse(authServiceSpy.isLoggedIn())
assertTrue(SettingsSyncStatusTracker.getInstance().currentStatus is SettingsSyncStatusTracker.SyncStatus.ActionRequired)
assertNotNull(authServiceSpy.getPendingUserAction("jba"))
}
@Test