mirror of
https://github.com/Eugeny/tabby.git
synced 2024-11-22 20:55:03 +03:00
experimental config sync
This commit is contained in:
parent
99ab8dacd4
commit
69115fb77a
@ -31,3 +31,4 @@ enableAutomaticUpdates: true
|
||||
version: 1
|
||||
vault: null
|
||||
encrypted: false
|
||||
enableExperimentalFeatures: false
|
||||
|
@ -136,9 +136,11 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
profilesService: ProfilesService,
|
||||
) {
|
||||
app.ready$.subscribe(() => {
|
||||
if (config.store.enableWelcomeTab) {
|
||||
app.openNewTabRaw({ type: WelcomeTabComponent })
|
||||
}
|
||||
config.ready$.toPromise().then(() => {
|
||||
if (config.store.enableWelcomeTab) {
|
||||
app.openNewTabRaw({ type: WelcomeTabComponent })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
platform.setErrorHandler(err => {
|
||||
|
@ -194,7 +194,6 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
async save (): Promise<void> {
|
||||
this.store.__cleanup()
|
||||
// Scrub undefined values
|
||||
let cleanStore = JSON.parse(JSON.stringify(this._store))
|
||||
cleanStore = await this.maybeEncryptConfig(cleanStore)
|
||||
@ -238,7 +237,7 @@ export class ConfigService {
|
||||
const module = imp.ngModule || imp
|
||||
if (module.ɵinj?.providers) {
|
||||
this.servicesCache[module.pluginName] = module.ɵinj.providers.map(provider => {
|
||||
return provider.useClass || provider
|
||||
return provider.useClass ?? provider.useExisting ?? provider
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -382,10 +381,12 @@ export class ConfigService {
|
||||
}
|
||||
delete decryptedVault.config.vault
|
||||
delete decryptedVault.config.encrypted
|
||||
delete decryptedVault.config.configSync
|
||||
return {
|
||||
...decryptedVault.config,
|
||||
vault: store.vault,
|
||||
encrypted: store.encrypted,
|
||||
configSync: store.configSync,
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,9 +401,11 @@ export class ConfigService {
|
||||
vault.config = { ...store }
|
||||
delete vault.config.vault
|
||||
delete vault.config.encrypted
|
||||
delete vault.config.configSync
|
||||
return {
|
||||
vault: await this.vault.encrypt(vault),
|
||||
encrypted: true,
|
||||
configSync: store.configSync,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import { ElectronHostWindow } from './services/hostWindow.service'
|
||||
import { ElectronFileProvider } from './services/fileProvider.service'
|
||||
import { ElectronHostAppService } from './services/hostApp.service'
|
||||
import { ElectronService } from './services/electron.service'
|
||||
import { ElectronHotkeyProvider } from './hotkeys'
|
||||
// import { ElectronHotkeyProvider } from './hotkeys'
|
||||
import { ElectronConfigProvider } from './config'
|
||||
|
||||
@NgModule({
|
||||
@ -24,7 +24,7 @@ import { ElectronConfigProvider } from './config'
|
||||
{ provide: LogService, useClass: ElectronLogService },
|
||||
{ provide: UpdaterService, useClass: ElectronUpdaterService },
|
||||
{ provide: DockingService, useClass: ElectronDockingService },
|
||||
{ provide: HotkeyProvider, useClass: ElectronHotkeyProvider, multi: true },
|
||||
// { provide: HotkeyProvider, useClass: ElectronHotkeyProvider, multi: true },
|
||||
{ provide: ConfigProvider, useClass: ElectronConfigProvider, multi: true },
|
||||
{ provide: FileProvider, useClass: ElectronFileProvider, multi: true },
|
||||
],
|
||||
|
@ -8,7 +8,7 @@ import { PluginManagerService } from '../services/pluginManager.service'
|
||||
|
||||
enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' }
|
||||
|
||||
const FORCE_ENABLE = ['tabby-core', 'tabby-settings']
|
||||
const FORCE_ENABLE = ['tabby-core', 'tabby-settings', 'tabby-electron', 'tabby-web']
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
|
@ -0,0 +1,116 @@
|
||||
h3.mb-3 Config sync
|
||||
|
||||
ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Sync
|
||||
ng-template(ngbNavContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Sync host
|
||||
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='config.store.configSync.host',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Secret sync token
|
||||
.description Get it from the Tabby Web settings window
|
||||
|
||||
.input-group
|
||||
input.form-control(
|
||||
type='password',
|
||||
[(ngModel)]='config.store.configSync.token',
|
||||
(ngModelChange)='config.save(); testConnection()'
|
||||
)
|
||||
.input-group-append(*ngIf='config.store.configSync.token')
|
||||
.input-group-text
|
||||
i.fas.fa-fw.fa-circle-notch.fa-spin.text-warning(*ngIf='connectionSuccessful === null')
|
||||
i.fas.fa-fw.fa-check.text-success(*ngIf='connectionSuccessful')
|
||||
i.fas.fa-fw.fa-exclamation-triangle.text-danger(*ngIf='connectionSuccessful === false')
|
||||
|
||||
ng-container(*ngIf='config.store.configSync.token')
|
||||
.alert.alert-danger(*ngIf='connectionSuccessful === false')
|
||||
i.fas.fa-exclamation-triangle
|
||||
span.ml-2 Connection failed: {{connectionError}}
|
||||
|
||||
ng-container(*ngIf='connectionSuccessful')
|
||||
.form-line
|
||||
.header
|
||||
.title Configs
|
||||
|
||||
div(*ngIf='configs === null')
|
||||
i.fas.fa-fw.fa-circle-notch.fa-spin
|
||||
span.ml-2 Loading configs...
|
||||
|
||||
ng-container(*ngIf='configs !== null')
|
||||
.list-group-light
|
||||
.list-group-item.d-flex.align-items-center(
|
||||
*ngFor='let cfg of configs',
|
||||
[class.active]='cfg.id === config.store.configSync.configID',
|
||||
)
|
||||
i.fas.fa-fw.fa-file
|
||||
.ml-2.d-flex.flex-column.align-items-start
|
||||
div {{cfg.name}}
|
||||
small.text-muted Modified on {{cfg.modified_at|date:'medium'}}
|
||||
.badge.badge-info(*ngIf='cfg.id === config.store.configSync.configID') ACTIVE
|
||||
.mr-auto
|
||||
button.btn.btn-link.ml-1(
|
||||
(click)='uploadAndSync(cfg)',
|
||||
[class.hover-reveal]='cfg.id !== config.store.configSync.configID'
|
||||
)
|
||||
i.fas.fa-arrow-up
|
||||
span.ml-2 Upload
|
||||
button.btn.btn-link.ml-1(
|
||||
(click)='downloadAndSync(cfg)',
|
||||
[class.hover-reveal]='cfg.id !== config.store.configSync.configID'
|
||||
)
|
||||
i.fas.fa-arrow-down
|
||||
span.ml-2 Download
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
href='#',
|
||||
(click)='uploadAsNew()'
|
||||
)
|
||||
i.fas.fa-fw.fa-cloud-upload-alt
|
||||
.ml-2 Upload as a new config
|
||||
|
||||
ng-container(*ngIf='config.store.configSync.configID')
|
||||
.form-line
|
||||
.header
|
||||
.title Sync automatically
|
||||
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.auto',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink) Advanced
|
||||
ng-template(ngbNavContent)
|
||||
.form-line
|
||||
.header
|
||||
.title Sync hotkeys
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.hotkeys',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Sync window settings
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.appearance',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.header
|
||||
.title Sync Vault
|
||||
toggle(
|
||||
[(ngModel)]='config.store.configSync.parts.vault',
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
div([ngbNavOutlet]='nav')
|
@ -0,0 +1,99 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { BaseComponent, ConfigService, PromptModalComponent, HostAppService, PlatformService, NotificationsService } from 'tabby-core'
|
||||
import { Config, ConfigSyncService } from '../services/configSync.service'
|
||||
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
selector: 'config-sync-settings-tab',
|
||||
template: require('./configSyncSettingsTab.component.pug'),
|
||||
})
|
||||
export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
connectionSuccessful: boolean|null = null
|
||||
connectionError: Error|null = null
|
||||
configs: Config[]|null = null
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private configSync: ConfigSyncService,
|
||||
private hostApp: HostAppService,
|
||||
private ngbModal: NgbModal,
|
||||
private platform: PlatformService,
|
||||
private notifications: NotificationsService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async ngOnInit () {
|
||||
await this.testConnection()
|
||||
this.loadConfigs()
|
||||
}
|
||||
|
||||
async testConnection () {
|
||||
if (!this.config.store.configSync.host || !this.config.store.configSync.token) {
|
||||
return
|
||||
}
|
||||
this.connectionSuccessful = null
|
||||
try {
|
||||
await this.configSync.getUser()
|
||||
this.connectionSuccessful = true
|
||||
this.loadConfigs()
|
||||
} catch (e) {
|
||||
this.connectionSuccessful = false
|
||||
this.connectionError = e
|
||||
this.configs = null
|
||||
}
|
||||
}
|
||||
|
||||
async loadConfigs () {
|
||||
this.configs = await this.configSync.getConfigs()
|
||||
}
|
||||
|
||||
async uploadAsNew () {
|
||||
let name = `New config on ${this.hostApp.platform}`
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
modal.componentInstance.prompt = 'Name for the new config'
|
||||
modal.componentInstance.value = name
|
||||
name = (await modal.result)?.value
|
||||
if (!name) {
|
||||
return
|
||||
}
|
||||
const cfg = await this.configSync.createNewConfig(name)
|
||||
this.loadConfigs()
|
||||
this.configSync.setConfig(cfg)
|
||||
this.uploadAndSync(cfg)
|
||||
}
|
||||
|
||||
async uploadAndSync (cfg: Config) {
|
||||
if (this.config.store.configSync.configID !== cfg.id) {
|
||||
if ((await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Overwrite the config on the remote side and start syncing?',
|
||||
buttons: ['Overwrite remote and sync', 'Cancel'],
|
||||
defaultId: 1,
|
||||
})).response === 1) {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.configSync.setConfig(cfg)
|
||||
await this.configSync.upload()
|
||||
this.loadConfigs()
|
||||
this.notifications.info('Config uploaded')
|
||||
}
|
||||
|
||||
async downloadAndSync (cfg: Config) {
|
||||
if ((await this.platform.showMessageBox({
|
||||
type: 'warning',
|
||||
message: 'Overwrite the local config and start syncing?',
|
||||
buttons: ['Overwrite local and sync', 'Cancel'],
|
||||
defaultId: 1,
|
||||
})).response === 1) {
|
||||
return
|
||||
}
|
||||
this.configSync.setConfig(cfg)
|
||||
await this.configSync.download()
|
||||
this.notifications.info('Config downloaded')
|
||||
}
|
||||
}
|
@ -15,15 +15,15 @@ button.btn.btn-warning.btn-block(*ngIf='config.restartRequested', '(click)'='res
|
||||
.text-muted {{homeBase.appVersion}}
|
||||
|
||||
.mb-5.mt-3
|
||||
button.btn.btn-secondary.mr-3((click)='homeBase.openGitHub()')
|
||||
button.btn.btn-secondary.mr-3.mb-2((click)='homeBase.openGitHub()')
|
||||
i.fab.fa-github
|
||||
span GitHub
|
||||
|
||||
button.btn.btn-secondary.mr-3((click)='homeBase.reportBug()')
|
||||
button.btn.btn-secondary.mr-3.mb-2((click)='homeBase.reportBug()')
|
||||
i.fas.fa-bug
|
||||
span Report a problem
|
||||
|
||||
button.btn.btn-secondary.mr-3(
|
||||
button.btn.btn-secondary.mr-3.mb-2(
|
||||
(click)='showReleaseNotes()',
|
||||
)
|
||||
i.fas.fa-book
|
||||
@ -90,7 +90,7 @@ button.btn.btn-warning.btn-block(*ngIf='config.restartRequested', '(click)'='res
|
||||
.d-flex.flex-column.w-100.h-100
|
||||
.h-100.d-flex
|
||||
.w-100.d-flex.flex-column
|
||||
h3 Config File
|
||||
h3 Config file
|
||||
textarea.form-control.h-100(
|
||||
[(ngModel)]='configFile'
|
||||
)
|
||||
|
@ -2,7 +2,19 @@ import { ConfigProvider, Platform } from 'tabby-core'
|
||||
|
||||
/** @hidden */
|
||||
export class SettingsConfigProvider extends ConfigProvider {
|
||||
defaults = { }
|
||||
defaults = {
|
||||
configSync: {
|
||||
host: 'https://tabby.sh',
|
||||
token: '',
|
||||
configID: null,
|
||||
auto: false,
|
||||
parts: {
|
||||
hotkeys: true,
|
||||
appearance: true,
|
||||
vault: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
platformDefaults = {
|
||||
[Platform.macOS]: {
|
||||
hotkeys: {
|
||||
|
@ -17,12 +17,15 @@ import { VaultSettingsTabComponent } from './components/vaultSettingsTab.compon
|
||||
import { SetVaultPassphraseModalComponent } from './components/setVaultPassphraseModal.component'
|
||||
import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component'
|
||||
import { ReleaseNotesComponent } from './components/releaseNotesTab.component'
|
||||
import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component'
|
||||
|
||||
import { ConfigSyncService } from './services/configSync.service'
|
||||
|
||||
import { SettingsTabProvider } from './api'
|
||||
import { ButtonProvider } from './buttonProvider'
|
||||
import { SettingsHotkeyProvider } from './hotkeys'
|
||||
import { SettingsConfigProvider } from './config'
|
||||
import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabProvider, ProfilesSettingsTabProvider } from './settings'
|
||||
import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabProvider, ProfilesSettingsTabProvider, ConfigSyncSettingsTabProvider } from './settings'
|
||||
|
||||
/** @hidden */
|
||||
@NgModule({
|
||||
@ -41,6 +44,7 @@ import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabP
|
||||
{ provide: SettingsTabProvider, useClass: WindowSettingsTabProvider, multi: true },
|
||||
{ provide: SettingsTabProvider, useClass: VaultSettingsTabProvider, multi: true },
|
||||
{ provide: SettingsTabProvider, useClass: ProfilesSettingsTabProvider, multi: true },
|
||||
{ provide: SettingsTabProvider, useClass: ConfigSyncSettingsTabProvider, multi: true },
|
||||
],
|
||||
entryComponents: [
|
||||
EditProfileModalComponent,
|
||||
@ -51,6 +55,7 @@ import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabP
|
||||
SetVaultPassphraseModalComponent,
|
||||
VaultSettingsTabComponent,
|
||||
WindowSettingsTabComponent,
|
||||
ConfigSyncSettingsTabComponent,
|
||||
ReleaseNotesComponent,
|
||||
],
|
||||
declarations: [
|
||||
@ -64,10 +69,13 @@ import { HotkeySettingsTabProvider, WindowSettingsTabProvider, VaultSettingsTabP
|
||||
SetVaultPassphraseModalComponent,
|
||||
VaultSettingsTabComponent,
|
||||
WindowSettingsTabComponent,
|
||||
ConfigSyncSettingsTabComponent,
|
||||
ReleaseNotesComponent,
|
||||
],
|
||||
})
|
||||
export default class SettingsModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||
export default class SettingsModule {
|
||||
constructor (public configSync: ConfigSyncService) { }
|
||||
}
|
||||
|
||||
export * from './api'
|
||||
export { SettingsTabComponent }
|
||||
|
179
tabby-settings/src/services/configSync.service.ts
Normal file
179
tabby-settings/src/services/configSync.service.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import * as yaml from 'js-yaml'
|
||||
import axios from 'axios'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ConfigService, HostAppService, Logger, LogService, Platform, PlatformService } from 'tabby-core'
|
||||
|
||||
export interface User {
|
||||
id: number
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
id: number
|
||||
name: string
|
||||
content: string
|
||||
last_used_with_version: string|null
|
||||
created_at: Date
|
||||
modified_at: Date
|
||||
}
|
||||
|
||||
const OPTIONAL_CONFIG_PARTS = ['hotkeys', 'appearance', 'vault']
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ConfigSyncService {
|
||||
private logger: Logger
|
||||
private lastRemoteChange = new Date(0)
|
||||
|
||||
constructor (
|
||||
log: LogService,
|
||||
private platform: PlatformService,
|
||||
private hostApp: HostAppService,
|
||||
private config: ConfigService,
|
||||
) {
|
||||
this.logger = log.create('configSync')
|
||||
config.ready$.toPromise().then(() => {
|
||||
this.autoSync()
|
||||
config.changed$.subscribe(() => {
|
||||
if (this.isEnabled() && this.config.store.configSync.auto) {
|
||||
this.upload()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
isAvailable (): boolean {
|
||||
return this.config.store.enableExperimentalFeatures && this.hostApp.platform !== Platform.Web
|
||||
}
|
||||
|
||||
isEnabled (): boolean {
|
||||
return this.isAvailable() &&
|
||||
!!this.config.store.configSync.host &&
|
||||
!!this.config.store.configSync.token &&
|
||||
!!this.config.store.configSync.configID
|
||||
}
|
||||
|
||||
async getConfigs (): Promise<Config[]> {
|
||||
return this.request('GET', '/api/1/configs')
|
||||
}
|
||||
|
||||
async getConfig (id: number): Promise<Config> {
|
||||
return this.request('GET', `/api/1/configs/${id}`)
|
||||
}
|
||||
|
||||
async updateConfig (id: number, data: Partial<Config>): Promise<Config> {
|
||||
return this.request('PATCH', `/api/1/configs/${id}`, { data })
|
||||
}
|
||||
|
||||
async getUser (): Promise<any> {
|
||||
return this.request('GET', '/api/1/user')
|
||||
}
|
||||
|
||||
async createNewConfig (name: string): Promise<Config> {
|
||||
return this.request('POST', '/api/1/configs', {
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
setConfig (config: Config): void {
|
||||
this.config.store.configSync.configID = config.id
|
||||
this.config.save()
|
||||
this.lastRemoteChange = new Date(config.modified_at)
|
||||
}
|
||||
|
||||
async upload (): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const data = this.readConfigDataForSync()
|
||||
const remoteData = yaml.load((await this.getConfig(this.config.store.configSync.configID)).content) as any
|
||||
for (const part of OPTIONAL_CONFIG_PARTS) {
|
||||
if (!this.config.store.configSync.parts[part]) {
|
||||
data[part] = remoteData[part]
|
||||
}
|
||||
}
|
||||
const content = yaml.dump(data)
|
||||
const result = await this.updateConfig(this.config.store.configSync.configID, {
|
||||
content,
|
||||
last_used_with_version: this.platform.getAppVersion(),
|
||||
})
|
||||
this.lastRemoteChange = new Date(result.modified_at)
|
||||
this.logger.debug('Config uploaded')
|
||||
} catch (error) {
|
||||
this.logger.error('Upload failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async download (): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const config = await this.getConfig(this.config.store.configSync.configID)
|
||||
const data = yaml.load(config.content) as any
|
||||
const localData = yaml.load(this.config.readRaw()) as any
|
||||
data.configSync = localData.configSync
|
||||
|
||||
for (const part of OPTIONAL_CONFIG_PARTS) {
|
||||
if (!this.config.store.configSync.parts[part]) {
|
||||
data[part] = localData[part]
|
||||
}
|
||||
}
|
||||
|
||||
this.writeConfigDataFromSync(data)
|
||||
this.logger.debug('Config downloaded')
|
||||
} catch (error) {
|
||||
this.logger.error('Download failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private readConfigDataForSync (): any {
|
||||
const data = yaml.load(this.config.readRaw()) as any
|
||||
delete data.configSync
|
||||
return data
|
||||
}
|
||||
|
||||
private writeConfigDataFromSync (data: any) {
|
||||
this.config.writeRaw(yaml.dump(data))
|
||||
}
|
||||
|
||||
private async request (method: 'GET'|'POST'|'PATCH', url: string, params = {}) {
|
||||
if (this.config.store.configSync.host.endsWith('/')) {
|
||||
this.config.store.configSync.host = this.config.store.configSync.host.slice(0, -1)
|
||||
}
|
||||
url = this.config.store.configSync.host + url
|
||||
this.logger.debug(`${method} ${url}`, params)
|
||||
try {
|
||||
const response = await axios.request({
|
||||
url,
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.config.store.configSync.token}`,
|
||||
},
|
||||
...params,
|
||||
})
|
||||
this.logger.debug(response)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
this.logger.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private async autoSync () {
|
||||
while (true) {
|
||||
if (this.isEnabled() && this.config.store.configSync.auto) {
|
||||
const cfg = await this.getConfig(this.config.store.configSync.configID)
|
||||
if (new Date(cfg.modified_at) > this.lastRemoteChange) {
|
||||
this.logger.info('Remote config changed, downloading')
|
||||
this.download()
|
||||
this.lastRemoteChange = new Date(cfg.modified_at)
|
||||
}
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 5000))
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,9 @@ import { SettingsTabProvider } from './api'
|
||||
import { HotkeySettingsTabComponent } from './components/hotkeySettingsTab.component'
|
||||
import { WindowSettingsTabComponent } from './components/windowSettingsTab.component'
|
||||
import { VaultSettingsTabComponent } from './components/vaultSettingsTab.component'
|
||||
import { ConfigSyncSettingsTabComponent } from './components/configSyncSettingsTab.component'
|
||||
import { ProfilesSettingsTabComponent } from './components/profilesSettingsTab.component'
|
||||
import { ConfigSyncService } from './services/configSync.service'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
@ -55,3 +57,22 @@ export class ProfilesSettingsTabProvider extends SettingsTabProvider {
|
||||
return ProfilesSettingsTabComponent
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class ConfigSyncSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'config-sync'
|
||||
icon = 'cloud'
|
||||
title = 'Config sync'
|
||||
|
||||
constructor (
|
||||
private configSync: ConfigSyncService,
|
||||
) { super() }
|
||||
|
||||
getComponentType (): any {
|
||||
if (!this.configSync.isAvailable()) {
|
||||
return null
|
||||
}
|
||||
return ConfigSyncSettingsTabComponent
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export class AppearanceSettingsTabProvider extends SettingsTabProvider {
|
||||
export class ColorSchemeSettingsTabProvider extends SettingsTabProvider {
|
||||
id = 'terminal-color-scheme'
|
||||
icon = 'palette'
|
||||
title = 'Color Scheme'
|
||||
title = 'Color scheme'
|
||||
|
||||
getComponentType (): any {
|
||||
return ColorSchemeSettingsTabComponent
|
||||
|
Loading…
Reference in New Issue
Block a user