mirror of
https://github.com/Eugeny/tabby-web.git
synced 2025-06-09 14:09:57 +00:00
.
This commit is contained in:
parent
c9aa8aac18
commit
5b64d034c4
@ -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,
|
||||||
|
@ -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].
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,3 +88,7 @@ a, button {
|
|||||||
lib-ngx-image-zoom {
|
lib-ngx-image-zoom {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngb-tooltip-window {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user