This commit is contained in:
Eugene Pankov 2021-10-22 22:35:15 +02:00
parent c9aa8aac18
commit 5b64d034c4
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
9 changed files with 110 additions and 49 deletions

View File

@ -1,5 +1,4 @@
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { NgbDropdownModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
@ -40,8 +39,6 @@ const ROUTES = [
BrowserAnimationsModule, BrowserAnimationsModule,
CommonModule, CommonModule,
FormsModule, FormsModule,
NgbDropdownModule,
NgbModalModule,
FontAwesomeModule, FontAwesomeModule,
ClipboardModule, ClipboardModule,
HttpClientModule, HttpClientModule,

View File

@ -1,14 +1,40 @@
.sidebar .sidebar
img.logo(src='{{_logo}}') img.logo(src='{{_logo}}')
button.btn.mt-auto((click)='openConfig()') button.btn.mt-auto(
fa-icon([icon]='_configIcon', [fixedWidth]='true') (click)='openConfig()',
placement='right',
ngbTooltip='Manage configs'
)
fa-icon([icon]='_configIcon', [fixedWidth]='true', size='lg')
button.btn((click)='openSettings()') button.btn(
fa-icon([icon]='_settingsIcon', [fixedWidth]='true') (click)='openSettings()',
*ngIf='loginService.user',
placement='right',
ngbTooltip='Settings'
)
fa-icon([icon]='_settingsIcon', [fixedWidth]='true', size='lg')
button.btn.mt-3((click)='logout()') a.btn.mt-3(
fa-icon([icon]='_logoutIcon', [fixedWidth]='true') href='/login',
*ngIf='!loginService.user',
placement='right',
ngbTooltip='Log in'
)
fa-icon([icon]='_loginIcon', [fixedWidth]='true', size='lg')
.terminal([hidden]='!showApp') button.btn.mt-3(
iframe(#iframe) (click)='logout()',
*ngIf='loginService.user',
placement='right',
ngbTooltip='Log out'
)
fa-icon([icon]='_logoutIcon', [fixedWidth]='true', size='lg')
.terminal
iframe(#iframe, [hidden]='!showApp')
.alert.alert-warning.d-flex.border-0.m-0(*ngIf='!loginService.user')
fa-icon.me-2([icon]='_saveIcon', [fixedWidth]='true')
div
div To save profiles and settings, #[a(href='/login') log in].

View File

@ -25,7 +25,7 @@
margin-bottom: 20px; margin-bottom: 20px;
} }
>button, >[ngbdropdown] > button { >.btn {
width: 64px; width: 64px;
height: 64px; height: 64px;
background: transparent; background: transparent;
@ -34,6 +34,10 @@
&::after { &::after {
display: none; display: none;
} }
&:hover {
color: white;
}
} }
} }
@ -41,19 +45,20 @@
flex: 1 1 0; flex: 1 1 0;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
} display: flex;
flex-direction: column;
iframe { > * {
flex: none;
}
> iframe {
background: $body-bg; background: $body-bg;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: none; border: none;
flex: 1 1 0;
}
} }
.config-menu { .config-menu {
.header { .header {
border-bottom: 1px solid black; border-bottom: 1px solid black;

View File

@ -3,14 +3,13 @@ import { HttpClient } from '@angular/common/http'
import { Title } from '@angular/platform-browser' import { Title } from '@angular/platform-browser'
import { AppConnectorService } from '../services/appConnector.service' import { AppConnectorService } from '../services/appConnector.service'
import { faCog, faFile, faPlus, faSignOutAlt } from '@fortawesome/free-solid-svg-icons' import { faCog, faFile, faPlus, faSave, faSignInAlt, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { SettingsModalComponent } from './settingsModal.component' import { SettingsModalComponent } from './settingsModal.component'
import { ConfigModalComponent } from './configModal.component' import { ConfigModalComponent } from './configModal.component'
import { ConfigService, LoginService } from 'src/common' import { ConfigService, LoginService } from 'src/common'
import { combineLatest } from 'rxjs' import { combineLatest } from 'rxjs'
import { Config, Version } from 'src/api' import { Config, Version } from 'src/api'
import { Router } from '@angular/router'
@Component({ @Component({
selector: 'main', selector: 'main',
@ -20,9 +19,11 @@ import { Router } from '@angular/router'
export class MainComponent { export class MainComponent {
_logo = require('../../../assets/logo.svg') _logo = require('../../../assets/logo.svg')
_settingsIcon = faCog _settingsIcon = faCog
_loginIcon = faSignInAlt
_logoutIcon = faSignOutAlt _logoutIcon = faSignOutAlt
_addIcon = faPlus _addIcon = faPlus
_configIcon = faFile _configIcon = faFile
_saveIcon = faSave
showApp = false showApp = false
@ -35,7 +36,6 @@ export class MainComponent {
public loginService: LoginService, public loginService: LoginService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private config: ConfigService, private config: ConfigService,
private router: Router,
) { ) {
titleService.setTitle('Tabby') titleService.setTitle('Tabby')
window.addEventListener('message', this.connectorRequestHandler) window.addEventListener('message', this.connectorRequestHandler)
@ -50,10 +50,6 @@ export class MainComponent {
async ngAfterViewInit () { async ngAfterViewInit () {
await this.loginService.ready$.toPromise() await this.loginService.ready$.toPromise()
if (!this.loginService.user) {
this.router.navigate(['/login'])
return
}
combineLatest( combineLatest(
this.config.activeConfig$, this.config.activeConfig$,
@ -63,7 +59,7 @@ export class MainComponent {
this.reloadApp(config, version) this.reloadApp(config, version)
} }
}) })
this.config
await this.config.ready$.toPromise() await this.config.ready$.toPromise()
await this.config.selectDefaultConfig() await this.config.selectDefaultConfig()
} }
@ -80,10 +76,12 @@ export class MainComponent {
async loadApp (config, version) { async loadApp (config, version) {
this.showApp = true this.showApp = true
this.iframe.nativeElement.src = '/terminal' this.iframe.nativeElement.src = '/terminal'
if (this.loginService.user) {
await this.http.patch(`/api/1/configs/${config.id}`, { await this.http.patch(`/api/1/configs/${config.id}`, {
last_used_with_version: version.version, last_used_with_version: version.version,
}).toPromise() }).toPromise()
} }
}
reloadApp (config: Config, version: Version) { reloadApp (config: Config, version: Version) {
// TODO check config incompatibility // TODO check config incompatibility

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { NgbDropdownModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap' import { NgbDropdownModule, NgbModalModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { RouterModule } from '@angular/router' import { RouterModule } from '@angular/router'
@ -31,6 +31,7 @@ const ROUTES = [
FormsModule, FormsModule,
NgbDropdownModule, NgbDropdownModule,
NgbModalModule, NgbModalModule,
NgbTooltipModule,
ClipboardModule, ClipboardModule,
FontAwesomeModule, FontAwesomeModule,
RouterModule.forChild(ROUTES), RouterModule.forChild(ROUTES),

View File

@ -39,7 +39,7 @@ export class SocketProxy {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async connect (options: any): Promise<void> { async connect (options: any): Promise<void> {
if (!this.loginService.user.is_pro && this.appConnector.sockets.length > this.appConnector.connectionLimit && !window.sessionStorage['upgrade-skip-active']) { if (!this.loginService.user?.is_pro && this.appConnector.sockets.length > this.appConnector.connectionLimit && !window.sessionStorage['upgrade-skip-active']) {
let skipped = false let skipped = false
try { try {
skipped = await this.zone.run(() => this.ngbModal.open(UpgradeModalComponent)).result skipped = await this.zone.run(() => this.ngbModal.open(UpgradeModalComponent)).result
@ -51,8 +51,8 @@ export class SocketProxy {
} }
this.options = options this.options = options
this.url = this.loginService.user.custom_connection_gateway this.url = this.loginService.user?.custom_connection_gateway
this.authToken = this.loginService.user.custom_connection_gateway_token this.authToken = this.loginService.user?.custom_connection_gateway_token
if (!this.url) { if (!this.url) {
try { try {
const gateway = await this.appConnector.chooseConnectionGateway() const gateway = await this.appConnector.chooseConnectionGateway()
@ -151,11 +151,14 @@ export class AppConnectorService {
private http: HttpClient, private http: HttpClient,
private commonService: CommonService, private commonService: CommonService,
private zone: NgZone, private zone: NgZone,
private loginService: LoginService,
) { ) {
this.configUpdate.pipe(debounceTime(1000)).subscribe(async content => { this.configUpdate.pipe(debounceTime(1000)).subscribe(async content => {
if (this.loginService.user) {
const result = await this.http.patch(`/api/1/configs/${this.config.id}`, { content }).toPromise() const result = await this.http.patch(`/api/1/configs/${this.config.id}`, { content }).toPromise()
Object.assign(this.config, result) Object.assign(this.config, result)
}
}) })
} }

View File

@ -30,14 +30,29 @@ export class ConfigService {
} }
async updateUser () { async updateUser () {
if (!this.loginService.user) {
return
}
await this.http.put('/api/1/user', this.user).toPromise() await this.http.put('/api/1/user', this.user).toPromise()
} }
async createNewConfig (): Promise<Config> { async createNewConfig (): Promise<Config> {
const config = await this.http.post('/api/1/configs', { const configData = {
content: '{}', content: '{}',
last_used_with_version: this._activeVersion?.version ?? this.getLatestStableVersion().version, last_used_with_version: this._activeVersion?.version ?? this.getLatestStableVersion().version,
}).toPromise() }
if (!this.loginService.user) {
const config = {
id: Date.now(),
name: `Temporary config at ${new Date()}`,
created_at: new Date(),
modified_at: new Date(),
...configData,
}
this.configs.push(config)
return config
}
const config = await this.http.post('/api/1/configs', configData).toPromise()
this.configs.push(config) this.configs.push(config)
return config return config
} }
@ -47,8 +62,11 @@ export class ConfigService {
} }
async duplicateActiveConfig () { async duplicateActiveConfig () {
const copy = {...this._activeConfig, pk: undefined} let copy = {...this._activeConfig, pk: undefined, id: undefined}
this.configs.push(await this.http.post('/api/1/configs', copy).toPromise()) if (this.loginService.user) {
copy = await this.http.post('/api/1/configs', copy).toPromise()
}
this.configs.push(copy)
} }
async selectVersion (version: Version) { async selectVersion (version: Version) {
@ -66,23 +84,29 @@ export class ConfigService {
this._activeConfig = config this._activeConfig = config
this.activeConfig$.next(config) this.activeConfig$.next(config)
this.selectVersion(matchingVersion) this.selectVersion(matchingVersion)
if (this.loginService.user) {
this.loginService.user.active_config = config.id this.loginService.user.active_config = config.id
await this.loginService.updateUser() await this.loginService.updateUser()
} }
}
async selectDefaultConfig () { async selectDefaultConfig () {
await this.ready$.toPromise() await this.ready$.toPromise()
await this.loginService.ready$.toPromise() await this.loginService.ready$.toPromise()
this.selectConfig(this.configs.find(c => c.id === this.loginService.user.active_config) ?? this.configs[0]) this.selectConfig(this.configs.find(c => c.id === this.loginService.user?.active_config) ?? this.configs[0])
} }
async deleteConfig (config: Config) { async deleteConfig (config: Config) {
if (this.loginService.user) {
await this.http.delete(`/api/1/configs/${config.id}`).toPromise() await this.http.delete(`/api/1/configs/${config.id}`).toPromise()
}
this.configs = this.configs.filter(x => x.id !== config.id) this.configs = this.configs.filter(x => x.id !== config.id)
} }
private async init () { private async init () {
if (this.loginService.user) {
this.configs = await this.http.get('/api/1/configs').toPromise() this.configs = await this.http.get('/api/1/configs').toPromise()
}
this.versions = await this.http.get('/api/1/versions').toPromise() this.versions = await this.http.get('/api/1/versions').toPromise()
this.versions.sort((a, b) => -semverCompare(a.version, b.version)) this.versions.sort((a, b) => -semverCompare(a.version, b.version))

View File

@ -6,7 +6,7 @@ import { User } from '../../api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class LoginService { export class LoginService {
user: User user: User | null
ready$ = new AsyncSubject<void>() ready$ = new AsyncSubject<void>()
constructor (private http: HttpClient) { constructor (private http: HttpClient) {
@ -14,6 +14,9 @@ export class LoginService {
} }
async updateUser () { async updateUser () {
if (!this.user) {
return
}
await this.http.put('/api/1/user', this.user).toPromise() await this.http.put('/api/1/user', this.user).toPromise()
} }

View File

@ -88,3 +88,7 @@ a, button {
lib-ngx-image-zoom { lib-ngx-image-zoom {
display: flex; display: flex;
} }
ngb-tooltip-window {
z-index: 1;
}