mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
github: use new accounts popup menu in clone dialog
GitOrigin-RevId: c0675f40f1aaccff61ef2d54abc4d25cbc952cb5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6368b78d7b
commit
800d84f37b
-114
@@ -1,114 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.ui.cloneDialog
|
||||
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.ui.JBPopupMenu
|
||||
import com.intellij.ui.SizedIcon
|
||||
import com.intellij.ui.components.JBMenu
|
||||
import com.intellij.util.IconUtil
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.cloneDialog.VcsCloneDialogUiSpec
|
||||
import icons.GithubIcons
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
|
||||
import org.jetbrains.plugins.github.api.data.GithubUser
|
||||
import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
import org.jetbrains.plugins.github.pullrequest.avatars.CachingGithubAvatarIconsProvider
|
||||
import org.jetbrains.plugins.github.util.CachingGithubUserAvatarLoader
|
||||
import org.jetbrains.plugins.github.util.GithubImageResizer
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
|
||||
internal class GHCloneDialogAccountsPanel(
|
||||
private val loginController: GHLoginController,
|
||||
private val authenticationManager: GithubAuthenticationManager,
|
||||
private val apiRequestExecutorManager: GithubApiRequestExecutorManager,
|
||||
private val avatarLoader: CachingGithubUserAvatarLoader,
|
||||
private val imageResizer: GithubImageResizer
|
||||
) : JPanel(FlowLayout(FlowLayout.LEADING, JBUI.scale(1), 0)) {
|
||||
|
||||
private val avatarSize = VcsCloneDialogUiSpec.Components.avatarSize
|
||||
private val defaultIcon = SizedIcon(GithubIcons.DefaultAvatar, avatarSize.get(), avatarSize.get())
|
||||
|
||||
private val accountComponents = HashMap<GithubAccount, JLabel>()
|
||||
private val userDetails = HashMap<GithubAccount, Pair<GithubUser, Icon>>()
|
||||
|
||||
private val popupMenuMouseAdapter = object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) = showPopupMenu()
|
||||
}
|
||||
|
||||
init {
|
||||
addMouseListener(popupMenuMouseAdapter)
|
||||
}
|
||||
|
||||
fun addAccount(account: GithubAccount) {
|
||||
if (accountComponents.isEmpty()) removeAll()
|
||||
|
||||
val label = accountComponents.getOrPut(account, {
|
||||
JLabel().apply {
|
||||
icon = defaultIcon
|
||||
toolTipText = account.name
|
||||
isOpaque = !isOpaque
|
||||
addMouseListener(popupMenuMouseAdapter)
|
||||
}
|
||||
})
|
||||
add(label)
|
||||
}
|
||||
|
||||
fun removeAccount(removedAccount: GithubAccount) {
|
||||
remove(accountComponents.remove(removedAccount))
|
||||
userDetails.remove(removedAccount)
|
||||
|
||||
revalidate()
|
||||
repaint()
|
||||
}
|
||||
|
||||
fun updateUserDetails(account: GithubAccount, user: GithubUser) {
|
||||
val iconsProvider = CachingGithubAvatarIconsProvider.Factory(
|
||||
avatarLoader,
|
||||
imageResizer,
|
||||
apiRequestExecutorManager.getExecutor(account)
|
||||
).create(avatarSize, this)
|
||||
val avatar = iconsProvider.getIcon(user.avatarUrl)
|
||||
userDetails[account] = Pair(user, avatar)
|
||||
accountComponents[account]?.icon = avatar
|
||||
}
|
||||
|
||||
private fun showPopupMenu() {
|
||||
// TODO: replace with custom popup action list
|
||||
val popupMenu = JBPopupMenu()
|
||||
for (account in authenticationManager.getAccounts()) {
|
||||
val accountSubmenu = JBMenu().apply {
|
||||
val pair = userDetails[account]
|
||||
if (pair == null) {
|
||||
text = account.name
|
||||
icon = scaleIcon(defaultIcon)
|
||||
add("Log in").addActionListener { loginController.reLogin(account) }
|
||||
addSeparator()
|
||||
add("Remove account").addActionListener { loginController.logout(account) }
|
||||
}
|
||||
else {
|
||||
val (user, avatar) = pair
|
||||
text = if (account.server.isGithubDotCom) account.name else ("${account.server.host}/${user.login}")
|
||||
icon = scaleIcon(avatar)
|
||||
add("Open on GitHub").addActionListener { BrowserUtil.browse(user.htmlUrl) }
|
||||
addSeparator()
|
||||
add("Log Out\u2026").addActionListener { loginController.logout(account) }
|
||||
}
|
||||
}
|
||||
popupMenu.add(accountSubmenu)
|
||||
}
|
||||
popupMenu.addSeparator()
|
||||
popupMenu.add("Add Account\u2026").addActionListener { loginController.addAccount() }
|
||||
popupMenu.show(this, 0, bounds.maxY.toInt())
|
||||
}
|
||||
|
||||
private fun scaleIcon(icon: Icon): Icon {
|
||||
val scale = JBUI.scale(20).toFloat() / icon.iconWidth.toFloat()
|
||||
return IconUtil.scale(icon, null, scale)
|
||||
}
|
||||
}
|
||||
+92
-16
@@ -5,6 +5,8 @@ import com.intellij.dvcs.repo.ClonePathProvider
|
||||
import com.intellij.dvcs.ui.CloneDvcsValidationUtils
|
||||
import com.intellij.dvcs.ui.DvcsBundle.getString
|
||||
import com.intellij.dvcs.ui.SelectChildTextFieldWithBrowseButton
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
@@ -14,6 +16,7 @@ import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.rd.attachChild
|
||||
import com.intellij.openapi.ui.DialogPanel
|
||||
import com.intellij.openapi.ui.ValidationInfo
|
||||
@@ -27,15 +30,23 @@ import com.intellij.ui.components.panels.Wrapper
|
||||
import com.intellij.ui.layout.*
|
||||
import com.intellij.ui.speedSearch.NameFilteringListModel
|
||||
import com.intellij.ui.speedSearch.SpeedSearch
|
||||
import com.intellij.util.IconUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.progress.ProgressVisibilityManager
|
||||
import com.intellij.util.ui.JBEmptyBorder
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.cloneDialog.AccountMenuItem
|
||||
import com.intellij.util.ui.cloneDialog.AccountMenuItem.Account
|
||||
import com.intellij.util.ui.cloneDialog.AccountMenuItem.Action
|
||||
import com.intellij.util.ui.cloneDialog.AccountMenuPopupStep
|
||||
import com.intellij.util.ui.cloneDialog.AccountsMenuListPopup
|
||||
import com.intellij.util.ui.cloneDialog.VcsCloneDialogUiSpec
|
||||
import git4idea.GitUtil
|
||||
import git4idea.checkout.GitCheckoutProvider
|
||||
import git4idea.commands.Git
|
||||
import git4idea.remote.GitRememberedInputs
|
||||
import icons.GithubIcons
|
||||
import org.jetbrains.plugins.github.api.*
|
||||
import org.jetbrains.plugins.github.api.data.GithubAuthenticatedUser
|
||||
import org.jetbrains.plugins.github.api.data.GithubRepo
|
||||
@@ -46,12 +57,18 @@ import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager
|
||||
import org.jetbrains.plugins.github.authentication.accounts.*
|
||||
import org.jetbrains.plugins.github.authentication.ui.GithubLoginPanel
|
||||
import org.jetbrains.plugins.github.exceptions.GithubMissingTokenException
|
||||
import org.jetbrains.plugins.github.pullrequest.avatars.CachingGithubAvatarIconsProvider
|
||||
import org.jetbrains.plugins.github.util.CachingGithubUserAvatarLoader
|
||||
import org.jetbrains.plugins.github.util.GithubImageResizer
|
||||
import org.jetbrains.plugins.github.util.GithubUrlUtil
|
||||
import org.jetbrains.plugins.github.util.handleOnEdt
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.event.ActionListener
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JSeparator
|
||||
import javax.swing.event.DocumentEvent
|
||||
@@ -63,29 +80,28 @@ internal class GHCloneDialogExtensionComponent(
|
||||
private val executorManager: GithubApiRequestExecutorManager,
|
||||
private val apiExecutorFactory: GithubApiRequestExecutor.Factory,
|
||||
private val accountInformationProvider: GithubAccountInformationProvider,
|
||||
avatarLoader: CachingGithubUserAvatarLoader,
|
||||
imageResizer: GithubImageResizer
|
||||
private val avatarLoader: CachingGithubUserAvatarLoader,
|
||||
private val imageResizer: GithubImageResizer
|
||||
) : VcsCloneDialogExtensionComponent() {
|
||||
private val LOG = logger<GHCloneDialogExtensionComponent>()
|
||||
|
||||
private val progressManager: ProgressVisibilityManager
|
||||
|
||||
// UI
|
||||
private val defaultAvatar = resizeIcon(GithubIcons.DefaultAvatar, VcsCloneDialogUiSpec.Components.avatarSize.get())
|
||||
private val defaultPopupAvatar = resizeIcon(GithubIcons.DefaultAvatar, VcsCloneDialogUiSpec.Components.popupMenuAvatarSize.get())
|
||||
|
||||
private val wrapper: Wrapper = Wrapper()
|
||||
private val repositoriesPanel: DialogPanel
|
||||
private val repositoryList: GHRepositoryList
|
||||
|
||||
private val accountsPanel: GHCloneDialogAccountsPanel = GHCloneDialogAccountsPanel(
|
||||
object : GHLoginController {
|
||||
override fun addAccount() = switchToLogin()
|
||||
override fun reLogin(account: GithubAccount) = switchToLogin(account)
|
||||
override fun logout(account: GithubAccount) = authenticationManager.removeAccount(account)
|
||||
},
|
||||
authenticationManager,
|
||||
executorManager,
|
||||
avatarLoader,
|
||||
imageResizer
|
||||
)
|
||||
private val popupMenuMouseAdapter = object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) = showPopupMenu()
|
||||
}
|
||||
|
||||
private val accountsPanel: JPanel = JPanel(FlowLayout(FlowLayout.LEADING, JBUI.scale(1), 0)).apply {
|
||||
addMouseListener(popupMenuMouseAdapter)
|
||||
}
|
||||
|
||||
private val searchField: SearchTextField = SearchTextField(false).apply {
|
||||
textEditor.emptyText.appendText("Search or enter a GitHub repository URL")
|
||||
@@ -109,6 +125,10 @@ internal class GHCloneDialogExtensionComponent(
|
||||
private var inLoginState = false
|
||||
private var selectedUrl by Delegates.observable<String?>(null) { _, _, _ -> onSelectedUrlChanged() }
|
||||
|
||||
// popup menu
|
||||
private val accountComponents = hashMapOf<GithubAccount, JLabel>()
|
||||
private val avatarsByAccount = hashMapOf<GithubAccount, Icon>()
|
||||
|
||||
init {
|
||||
val speedSearch = SpeedSearch()
|
||||
|
||||
@@ -214,7 +234,16 @@ internal class GHCloneDialogExtensionComponent(
|
||||
|
||||
private fun addAccount(account: GithubAccount) {
|
||||
repositoriesByAccount.remove(account)
|
||||
accountsPanel.addAccount(account)
|
||||
|
||||
val label = accountComponents.getOrPut(account) {
|
||||
JLabel().apply {
|
||||
icon = defaultAvatar
|
||||
toolTipText = account.name
|
||||
isOpaque = !isOpaque
|
||||
addMouseListener(popupMenuMouseAdapter)
|
||||
}
|
||||
}
|
||||
accountsPanel.add(label)
|
||||
|
||||
try {
|
||||
val executor = executorManager.getExecutor(account)
|
||||
@@ -232,7 +261,11 @@ internal class GHCloneDialogExtensionComponent(
|
||||
|
||||
private fun removeAccount(account: GithubAccount) {
|
||||
repositoriesByAccount.remove(account)
|
||||
accountsPanel.removeAccount(account)
|
||||
accountComponents.remove(account).let {
|
||||
accountsPanel.remove(it)
|
||||
accountsPanel.revalidate()
|
||||
accountsPanel.repaint()
|
||||
}
|
||||
refillRepositories()
|
||||
if (!authenticationManager.hasAccounts()) switchToLogin()
|
||||
}
|
||||
@@ -241,14 +274,20 @@ internal class GHCloneDialogExtensionComponent(
|
||||
executor: GithubApiRequestExecutor.WithTokenAuth) {
|
||||
progressManager.run(object : Task.Backgroundable(project, "Not Visible") {
|
||||
lateinit var user: GithubAuthenticatedUser
|
||||
lateinit var iconProvider: CachingGithubAvatarIconsProvider
|
||||
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
user = accountInformationProvider.getInformation(executor, indicator, account)
|
||||
iconProvider = CachingGithubAvatarIconsProvider
|
||||
.Factory(avatarLoader, imageResizer, executor)
|
||||
.create(VcsCloneDialogUiSpec.Components.popupMenuAvatarSize, accountsPanel)
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
userDetailsByAccount[account] = user
|
||||
accountsPanel.updateUserDetails(account, user)
|
||||
val avatar = iconProvider.getIcon(user.avatarUrl)
|
||||
avatarsByAccount[account] = avatar
|
||||
accountComponents[account]?.icon = resizeIcon(avatar, VcsCloneDialogUiSpec.Components.avatarSize.get())
|
||||
refillRepositories()
|
||||
}
|
||||
|
||||
@@ -452,4 +491,41 @@ internal class GHCloneDialogExtensionComponent(
|
||||
return !wasThere
|
||||
}
|
||||
}
|
||||
|
||||
private fun resizeIcon(icon: Icon, size: Int): Icon {
|
||||
val scale = JBUI.scale(size).toFloat() / icon.iconWidth.toFloat()
|
||||
return IconUtil.scale(icon, null, scale)
|
||||
}
|
||||
|
||||
private fun showPopupMenu() {
|
||||
val menuItems = mutableListOf<AccountMenuItem>()
|
||||
val project = ProjectManager.getInstance().defaultProject
|
||||
|
||||
for ((index, account) in authenticationManager.getAccounts().withIndex()) {
|
||||
val user = userDetailsByAccount[account]
|
||||
|
||||
val accountTitle = user?.login ?: account.name
|
||||
val serverInfo = account.server.toUrl().removePrefix("http://").removePrefix("https://")
|
||||
val avatar = avatarsByAccount[account] ?: defaultPopupAvatar
|
||||
val accountActions = mutableListOf<Action>()
|
||||
val showSeparatorAbove = index != 0
|
||||
|
||||
if (user == null) {
|
||||
accountActions += Action("Log in\u2026", { switchToLogin(account) })
|
||||
accountActions += Action("Remove account", { authenticationManager.removeAccount(account) }, showSeparatorAbove = true)
|
||||
}
|
||||
else {
|
||||
if (account != authenticationManager.getDefaultAccount(project)) {
|
||||
accountActions += Action("Set as Default", { authenticationManager.setDefaultAccount(project, account) })
|
||||
}
|
||||
accountActions += Action("Open on GitHub", { BrowserUtil.browse(user.htmlUrl) }, AllIcons.Ide.External_link_arrow)
|
||||
accountActions += Action("Log Out\u2026", { authenticationManager.removeAccount(account) }, showSeparatorAbove = true)
|
||||
}
|
||||
|
||||
menuItems += Account(accountTitle, serverInfo, avatar, accountActions, showSeparatorAbove)
|
||||
}
|
||||
menuItems += Action("Add Account\u2026", { switchToLogin() }, showSeparatorAbove = true)
|
||||
|
||||
AccountsMenuListPopup(null, AccountMenuPopupStep(menuItems)).showUnderneathOf(accountsPanel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.ui.cloneDialog
|
||||
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
|
||||
internal interface GHLoginController {
|
||||
fun addAccount()
|
||||
|
||||
fun reLogin(account: GithubAccount)
|
||||
|
||||
fun logout(account: GithubAccount)
|
||||
}
|
||||
Reference in New Issue
Block a user