pluginsSettingsTab.component.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
  2. import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
  3. import { BehaviorSubject, Observable, debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rxjs'
  4. import semverGt from 'semver/functions/gt'
  5. import { Component, HostBinding, Input } from '@angular/core'
  6. import { ConfigService, PlatformService, PluginInfo } from 'tabby-core'
  7. import { PluginManagerService } from '../services/pluginManager.service'
  8. enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' }
  9. const FORCE_ENABLE = ['tabby-core', 'tabby-settings', 'tabby-electron', 'tabby-web', 'tabby-plugin-manager']
  10. _('Search plugins')
  11. /** @hidden */
  12. @Component({
  13. templateUrl: './pluginsSettingsTab.component.pug',
  14. styleUrls: ['./pluginsSettingsTab.component.scss'],
  15. })
  16. export class PluginsSettingsTabComponent {
  17. BusyState = BusyState
  18. @Input() availablePlugins$: Observable<PluginInfo[]>
  19. @Input() availablePluginsQuery$ = new BehaviorSubject<string>('')
  20. @Input() availablePluginsReady = false
  21. @Input() installedPluginsQuery$ = new BehaviorSubject<string>('')
  22. @Input() knownUpgrades: Record<string, PluginInfo|null> = {}
  23. @Input() busy = new Map<string, BusyState>()
  24. @Input() erroredPlugin: string
  25. @Input() errorMessage: string
  26. @HostBinding('class.content-box') true
  27. installedPlugins$: PluginInfo[] = []
  28. installedFilter = ''
  29. availableFilter = ''
  30. constructor (
  31. private config: ConfigService,
  32. private platform: PlatformService,
  33. public pluginManager: PluginManagerService,
  34. ) {
  35. }
  36. ngOnInit () {
  37. this.availablePlugins$ = this.availablePluginsQuery$
  38. .asObservable()
  39. .pipe(
  40. debounceTime(200),
  41. distinctUntilChanged(),
  42. flatMap(query => {
  43. this.availablePluginsReady = false
  44. return this.pluginManager.listAvailable(query).pipe(tap(() => {
  45. this.availablePluginsReady = true
  46. }))
  47. }),
  48. )
  49. this.availablePlugins$.pipe(first(), map((plugins: PluginInfo[]) => {
  50. plugins.sort((a, b) => a.name > b.name ? 1 : -1)
  51. return plugins
  52. })).subscribe(available => {
  53. for (const plugin of this.pluginManager.installedPlugins) {
  54. this.knownUpgrades[plugin.name] = available.find(x => x.name === plugin.name && semverGt(x.version, plugin.version)) ?? null
  55. }
  56. })
  57. this.installedPluginsQuery$
  58. .asObservable()
  59. .pipe(
  60. debounceTime(200),
  61. distinctUntilChanged(),
  62. flatMap(query => {
  63. return this.pluginManager.listInstalled(query)
  64. }),
  65. ).subscribe(plugin => {
  66. this.installedPlugins$ = plugin
  67. })
  68. }
  69. openPluginsFolder (): void {
  70. this.platform.openPath(this.pluginManager.userPluginsPath)
  71. }
  72. searchAvailable (query: string) {
  73. this.availablePluginsQuery$.next(query)
  74. }
  75. searchInstalled (query: string) {
  76. this.installedPluginsQuery$.next(query)
  77. }
  78. isAlreadyInstalled (plugin: PluginInfo): boolean {
  79. return this.pluginManager.installedPlugins.some(x => x.name === plugin.name)
  80. }
  81. async installPlugin (plugin: PluginInfo): Promise<void> {
  82. this.busy.set(plugin.name, BusyState.Installing)
  83. try {
  84. await this.pluginManager.installPlugin(plugin)
  85. this.busy.delete(plugin.name)
  86. this.config.requestRestart()
  87. } catch (err) {
  88. this.erroredPlugin = plugin.name
  89. this.errorMessage = err
  90. this.busy.delete(plugin.name)
  91. throw err
  92. }
  93. }
  94. async uninstallPlugin (plugin: PluginInfo): Promise<void> {
  95. this.busy.set(plugin.name, BusyState.Uninstalling)
  96. try {
  97. await this.pluginManager.uninstallPlugin(plugin)
  98. this.busy.delete(plugin.name)
  99. this.config.requestRestart()
  100. } catch (err) {
  101. this.erroredPlugin = plugin.name
  102. this.errorMessage = err
  103. this.busy.delete(plugin.name)
  104. throw err
  105. }
  106. }
  107. async upgradePlugin (plugin: PluginInfo): Promise<void> {
  108. return this.installPlugin(this.knownUpgrades[plugin.name]!)
  109. }
  110. showPluginInfo (plugin: PluginInfo) {
  111. this.platform.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
  112. }
  113. showPluginHomepage (plugin: PluginInfo) {
  114. this.platform.openExternal(plugin.homepage ?? '')
  115. }
  116. isPluginEnabled (plugin: PluginInfo) {
  117. return !this.config.store.pluginBlacklist.includes(plugin.name)
  118. }
  119. canDisablePlugin (plugin: PluginInfo) {
  120. return !FORCE_ENABLE.includes(plugin.packageName)
  121. }
  122. togglePlugin (plugin: PluginInfo) {
  123. if (this.isPluginEnabled(plugin)) {
  124. this.disablePlugin(plugin)
  125. } else {
  126. this.enablePlugin(plugin)
  127. }
  128. }
  129. enablePlugin (plugin: PluginInfo) {
  130. this.config.store.pluginBlacklist = this.config.store.pluginBlacklist.filter(x => x !== plugin.name)
  131. this.config.save()
  132. this.config.requestRestart()
  133. }
  134. disablePlugin (plugin: PluginInfo) {
  135. this.config.store.pluginBlacklist = [...this.config.store.pluginBlacklist, plugin.name]
  136. this.config.save()
  137. this.config.requestRestart()
  138. }
  139. }