mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
refactor: refactor sideload page (#2475)
* refactor: refactor sideload page * chore: improve ux * chore: update * chore: update
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
<div class="header montserrat">
|
||||
<img class="logo" alt="" [src]="pkg | mimeType | trustUrl" />
|
||||
<div class="text">
|
||||
<h1 ticker class="title">{{ pkg.manifest.title }}</h1>
|
||||
<p class="version">{{ pkg.manifest.version | displayEmver }}</p>
|
||||
<p *ngIf="pkg['published-at'] as published" class="published">
|
||||
Released: {{ published | date : 'medium' }}
|
||||
</p>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<img class="logo" alt="" [src]="pkg | mimeType | trustUrl" />
|
||||
<div class="text">
|
||||
<h1 ticker class="title">{{ pkg.manifest.title }}</h1>
|
||||
<p class="version">{{ pkg.manifest.version | displayEmver }}</p>
|
||||
<p *ngIf="pkg['published-at'] as published" class="published">
|
||||
Released: {{ published | date : 'medium' }}
|
||||
</p>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
.header {
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 16px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.text {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { MarketplacePkg } from '../types'
|
||||
})
|
||||
export class MimeTypePipe implements PipeTransform {
|
||||
transform(pkg: MarketplacePkg): string {
|
||||
if (pkg.icon.startsWith('data:')) return pkg.icon
|
||||
|
||||
if (pkg.manifest.assets.icon) {
|
||||
switch (pkg.manifest.assets.icon.split('.').pop()) {
|
||||
case 'png':
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
color: var(--tui-error-fill);
|
||||
}
|
||||
|
||||
[tuiWrapper][data-appearance='input-file'] {
|
||||
&:hover,
|
||||
&:active {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
tui-dialog {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import { Router, RouteReuseStrategy } from '@angular/router'
|
||||
import { IonicRouteStrategy, IonNav } from '@ionic/angular'
|
||||
import { TUI_DATE_FORMAT, TUI_DATE_SEPARATOR } from '@taiga-ui/cdk'
|
||||
import {
|
||||
tuiButtonOptionsProvider,
|
||||
tuiNumberFormatProvider,
|
||||
tuiTextfieldOptionsProvider,
|
||||
} from '@taiga-ui/core'
|
||||
import { tuiButtonOptionsProvider } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TUI_DATE_TIME_VALUE_TRANSFORMER,
|
||||
TUI_DATE_VALUE_TRANSFORMER,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
|
||||
import { SnekDirective } from './snek.directive'
|
||||
import { SnakePage } from './snake.page'
|
||||
import { TuiButtonModule } from '@taiga-ui/core'
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IonicModule, TuiButtonModule],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { TuiButtonModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { HomePage } from './home.page'
|
||||
|
||||
const ROUTES: Routes = [
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="outline"
|
||||
shape="rounded"
|
||||
size="xs"
|
||||
icon="tuiIconMoreHorizontal"
|
||||
iconLeft="tuiIconMoreHorizontal"
|
||||
[style.border-radius.%]="100"
|
||||
>
|
||||
Actions
|
||||
</button>
|
||||
|
||||
@@ -9,11 +9,11 @@ import {
|
||||
import {
|
||||
TuiBadgedContentModule,
|
||||
TuiBadgeNotificationModule,
|
||||
TuiButtonModule,
|
||||
} from '@taiga-ui/experimental'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { TickerModule } from '@start9labs/shared'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDataListModule,
|
||||
TuiHostedDropdownModule,
|
||||
TuiSvgModule,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDataListModule,
|
||||
TuiHostedDropdownModule,
|
||||
TuiSvgModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { AuthService } from 'src/app/services/auth.service'
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<ng-content></ng-content>
|
||||
<div class="toolbar">
|
||||
<button tuiIconButton icon="tuiIconCloudLarge" appearance="success">
|
||||
<button tuiIconButton iconLeft="tuiIconCloudLarge" appearance="success">
|
||||
Connection
|
||||
</button>
|
||||
<tui-badged-content size="m" [contentBottom]="4">
|
||||
<button tuiIconButton icon="tuiIconBellLarge" appearance="warning">
|
||||
<tui-badged-content [style.--tui-radius.%]="50">
|
||||
<tui-badge-notification tuiSlot="bottom" size="s">4</tui-badge-notification>
|
||||
<button tuiIconButton iconLeft="tuiIconBellLarge" appearance="warning">
|
||||
Notifications
|
||||
</button>
|
||||
</tui-badged-content>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { TuiBadgedContentModule } from '@taiga-ui/kit'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDataListModule,
|
||||
TuiHostedDropdownModule,
|
||||
TuiSvgModule,
|
||||
} from '@taiga-ui/core'
|
||||
import {
|
||||
TuiBadgedContentModule,
|
||||
TuiBadgeNotificationModule,
|
||||
TuiButtonModule,
|
||||
} from '@taiga-ui/experimental'
|
||||
import { HeaderMenuComponent } from './header-menu/header-menu.component'
|
||||
|
||||
@Component({
|
||||
@@ -16,6 +19,7 @@ import { HeaderMenuComponent } from './header-menu/header-menu.component'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
TuiBadgedContentModule,
|
||||
TuiBadgeNotificationModule,
|
||||
TuiButtonModule,
|
||||
TuiHostedDropdownModule,
|
||||
TuiDataListModule,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<button
|
||||
tuiIconButton
|
||||
size="xs"
|
||||
icon="tuiIconClose"
|
||||
iconLeft="tuiIconClose"
|
||||
appearance="icon"
|
||||
class="close"
|
||||
(click.stop.prevent)="removeTab(tab, rla.isActive)"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { Router, RouterModule } from '@angular/router'
|
||||
import { TuiButtonModule, TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { NavigationService } from '../../services/navigation.service'
|
||||
import { NavigationItem } from '../../types/navigation-item'
|
||||
|
||||
|
||||
@@ -8,17 +8,9 @@ export const SYSTEM_UTILITIES: Record<string, { icon: string; title: string }> =
|
||||
icon: 'tuiIconGlobeLarge',
|
||||
title: 'Updates',
|
||||
},
|
||||
'/portal/system/devices': {
|
||||
icon: 'assets/img/icon_transparent.png',
|
||||
title: 'Devices',
|
||||
},
|
||||
'/portal/system/metrics': {
|
||||
icon: 'assets/img/icon_transparent.png',
|
||||
title: 'Metrics',
|
||||
},
|
||||
'/portal/system/manual': {
|
||||
icon: 'assets/img/icon_transparent.png',
|
||||
title: 'Manual',
|
||||
'/portal/system/sideload': {
|
||||
icon: 'tuiIconUploadLarge',
|
||||
title: 'Sideload',
|
||||
},
|
||||
'/portal/system/snek': {
|
||||
icon: 'assets/img/icon_transparent.png',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { CopyService } from '@start9labs/shared'
|
||||
import { TuiButtonModule, TuiDialogContext } from '@taiga-ui/core'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus'
|
||||
import { QrCodeModule } from 'ng-qrcode'
|
||||
import { ActionResponse } from 'src/app/services/api/api.types'
|
||||
@@ -21,7 +22,7 @@ import { ActionResponse } from 'src/app/services/api/api.types'
|
||||
*ngIf="context.data.copyable"
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
icon="tuiIconCopyLarge"
|
||||
iconLeft="tuiIconCopyLarge"
|
||||
(click)="copyService.copy(context.data.value)"
|
||||
>
|
||||
Copy
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { TuiButtonModule, TuiDialogService } from '@taiga-ui/core'
|
||||
import { TuiDialogService } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { tuiPure } from '@taiga-ui/cdk'
|
||||
import { TUI_PROMPT } from '@taiga-ui/kit'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
} from '@angular/core'
|
||||
import { CopyService } from '@start9labs/shared'
|
||||
import { mask } from 'src/app/util/mask'
|
||||
import { TuiButtonModule, TuiLabelModule } from '@taiga-ui/core'
|
||||
import { TuiLabelModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
|
||||
@Component({
|
||||
selector: 'service-credential',
|
||||
@@ -17,7 +18,7 @@ import { TuiButtonModule, TuiLabelModule } from '@taiga-ui/core'
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
[icon]="masked ? 'tuiIconEyeLarge' : 'tuiIconEyeOffLarge'"
|
||||
[iconLeft]="masked ? 'tuiIconEyeLarge' : 'tuiIconEyeOffLarge'"
|
||||
(click)="masked = !masked"
|
||||
>
|
||||
Toggle
|
||||
@@ -25,7 +26,7 @@ import { TuiButtonModule, TuiLabelModule } from '@taiga-ui/core'
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
icon="tuiIconCopyLarge"
|
||||
iconLeft="tuiIconCopyLarge"
|
||||
(click)="copyService.copy(value)"
|
||||
>
|
||||
Copy
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
inject,
|
||||
Input,
|
||||
} from '@angular/core'
|
||||
import { TuiButtonModule, TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { InterfaceInfo } from 'src/app/services/patch-db/data-model'
|
||||
import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
|
||||
@@ -23,7 +24,7 @@ import { ExtendedInterfaceInfo } from '../pipes/interface-info.pipe'
|
||||
*ngIf="info.type === 'ui'"
|
||||
tuiIconButton
|
||||
appearance="flat"
|
||||
icon="tuiIconExternalLinkLarge"
|
||||
iconLeft="tuiIconExternalLinkLarge"
|
||||
[style.border-radius.%]="100"
|
||||
(click.stop.prevent)="launchUI(info)"
|
||||
[disabled]="disabled"
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
LoadingService,
|
||||
} from '@start9labs/shared'
|
||||
import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogContext,
|
||||
TuiDialogService,
|
||||
TuiLoaderModule,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ErrorService, SharedPipesModule } from '@start9labs/shared'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import { TuiButtonModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Output,
|
||||
} from '@angular/core'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import { TuiButtonModule, TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { UnknownDisk } from 'src/app/services/api/api.types'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { UnitConversionPipesModule } from '@start9labs/shared'
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
Output,
|
||||
} from '@angular/core'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogOptions,
|
||||
TuiDialogService,
|
||||
TuiSvgModule,
|
||||
@@ -52,7 +52,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe'
|
||||
tuiIconButton
|
||||
size="xs"
|
||||
appearance="icon"
|
||||
icon="tuiIconEdit2"
|
||||
iconLeft="tuiIconEdit2"
|
||||
(click)="update.emit(target)"
|
||||
>
|
||||
Update
|
||||
@@ -61,7 +61,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe'
|
||||
tuiIconButton
|
||||
size="xs"
|
||||
appearance="icon"
|
||||
icon="tuiIconTrash2"
|
||||
iconLeft="tuiIconTrash2"
|
||||
(click)="delete$.next(target.id)"
|
||||
>
|
||||
Delete
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Component, inject } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogContext,
|
||||
TuiDialogOptions,
|
||||
TuiGroupModule,
|
||||
TuiLoaderModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiCheckboxBlockModule } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
|
||||
@@ -36,6 +36,7 @@ import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
|
||||
appearance="secondary"
|
||||
type="button"
|
||||
class="button"
|
||||
size="l"
|
||||
(click)="selectTarget()"
|
||||
>
|
||||
Target
|
||||
@@ -48,6 +49,7 @@ import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
|
||||
appearance="secondary"
|
||||
type="button"
|
||||
class="button"
|
||||
size="l"
|
||||
(click)="selectPackages()"
|
||||
>
|
||||
Packages
|
||||
@@ -70,7 +72,6 @@ import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
|
||||
</div>
|
||||
<button
|
||||
tuiButton
|
||||
size="m"
|
||||
class="submit"
|
||||
[style.margin-left]="'auto'"
|
||||
(click)="save()"
|
||||
|
||||
@@ -8,13 +8,8 @@ import {
|
||||
ALWAYS_TRUE_HANDLER,
|
||||
TuiForModule,
|
||||
} from '@taiga-ui/cdk'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogService,
|
||||
TuiLinkModule,
|
||||
TuiSvgModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiFadeModule } from '@taiga-ui/experimental'
|
||||
import { TuiDialogService, TuiLinkModule, TuiSvgModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental'
|
||||
import { TuiCheckboxModule } from '@taiga-ui/kit'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { BackupRun } from 'src/app/services/api/api.types'
|
||||
@@ -31,7 +26,6 @@ import { REPORT } from './report.component'
|
||||
Past Events
|
||||
<button
|
||||
tuiButton
|
||||
size="m"
|
||||
appearance="secondary-destructive"
|
||||
[disabled]="disabled"
|
||||
(click)="delete()"
|
||||
|
||||
@@ -3,13 +3,12 @@ import { Component, inject, OnInit } from '@angular/core'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogOptions,
|
||||
TuiDialogService,
|
||||
TuiNotificationModule,
|
||||
TuiSvgModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiFadeModule } from '@taiga-ui/experimental'
|
||||
import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental'
|
||||
import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit'
|
||||
import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus'
|
||||
import { BehaviorSubject, filter } from 'rxjs'
|
||||
@@ -36,7 +35,7 @@ import { EDIT } from './edit.component'
|
||||
</tui-notification>
|
||||
<h3 class="g-title">
|
||||
Saved Jobs
|
||||
<button tuiButton size="s" icon="tuiIconPlus" (click)="create()">
|
||||
<button tuiButton size="s" iconLeft="tuiIconPlus" (click)="create()">
|
||||
Create New Job
|
||||
</button>
|
||||
</h3>
|
||||
@@ -65,14 +64,14 @@ import { EDIT } from './edit.component'
|
||||
tuiIconButton
|
||||
appearance="icon"
|
||||
size="xs"
|
||||
icon="tuiIconEdit2"
|
||||
iconLeft="tuiIconEdit2"
|
||||
(click)="update(job)"
|
||||
></button>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="icon"
|
||||
size="xs"
|
||||
icon="tuiIconTrash2"
|
||||
iconLeft="tuiIconTrash2"
|
||||
(click)="delete(job.id)"
|
||||
></button>
|
||||
</td>
|
||||
|
||||
@@ -2,11 +2,8 @@ import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogContext,
|
||||
TuiGroupModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiDialogContext, TuiGroupModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiCheckboxBlockModule } from '@taiga-ui/kit'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
|
||||
@@ -2,8 +2,8 @@ import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ErrorService } from '@start9labs/shared'
|
||||
import { TuiForModule } from '@taiga-ui/cdk'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogContext,
|
||||
TuiDialogOptions,
|
||||
TuiDialogService,
|
||||
|
||||
@@ -5,18 +5,10 @@ import {
|
||||
unionSelectKey,
|
||||
unionValueKey,
|
||||
} from '@start9labs/start-sdk/lib/config/configTypes'
|
||||
import { TuiButtonModule, TuiNotificationModule } from '@taiga-ui/core'
|
||||
import { TuiNotificationModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental'
|
||||
import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus'
|
||||
import {
|
||||
BehaviorSubject,
|
||||
catchError,
|
||||
from,
|
||||
Observable,
|
||||
of,
|
||||
share,
|
||||
startWith,
|
||||
switchMap,
|
||||
} from 'rxjs'
|
||||
import { BehaviorSubject } from 'rxjs'
|
||||
import { FormPage } from 'src/app/apps/ui/modals/form/form.page'
|
||||
import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
|
||||
import {
|
||||
@@ -37,7 +29,6 @@ import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { BackupConfig } from '../types/backup-config'
|
||||
import { BackupsPhysicalComponent } from '../components/physical.component'
|
||||
import { BackupsTargetsComponent } from '../components/targets.component'
|
||||
import { TuiFadeModule } from '@taiga-ui/experimental'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { EmverPipesModule } from '@start9labs/shared'
|
||||
import {
|
||||
TuiAvatarModule,
|
||||
TuiCellModule,
|
||||
TuiTitleModule,
|
||||
} from '@taiga-ui/experimental'
|
||||
|
||||
@Component({
|
||||
selector: 'sideload-dependencies',
|
||||
template: `
|
||||
<h3 class="g-title" [style.text-indent.rem]="1">Dependencies</h3>
|
||||
<div *ngFor="let dep of package.manifest.dependencies | keyvalue" tuiCell>
|
||||
<tui-avatar [src]="getImage(dep.key)"></tui-avatar>
|
||||
<div tuiTitle>
|
||||
<div>
|
||||
<strong>{{ getTitle(dep.key) }} </strong>
|
||||
<ng-container [ngSwitch]="dep.value.requirement.type">
|
||||
<span *ngSwitchCase="'required'">(required)</span>
|
||||
<span *ngSwitchCase="'opt-out'">(required by default)</span>
|
||||
<span *ngSwitchCase="'opt-in'">(optional)</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div tuiSubtitle [style.color]="'var(--tui-text-03)'">
|
||||
{{ dep.value.version | displayEmver }}
|
||||
</div>
|
||||
<div tuiSubtitle>
|
||||
{{ dep.value.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
TuiTitleModule,
|
||||
EmverPipesModule,
|
||||
TuiAvatarModule,
|
||||
TuiCellModule,
|
||||
],
|
||||
})
|
||||
export class SideloadDependenciesComponent {
|
||||
@Input({ required: true })
|
||||
package!: MarketplacePkg
|
||||
|
||||
getTitle(key: string): string {
|
||||
return this.package['dependency-metadata'][key]?.title || key
|
||||
}
|
||||
|
||||
getImage(key: string): string {
|
||||
const icon = this.package['dependency-metadata'][key]?.icon
|
||||
|
||||
return icon ? `data:image/png;base64,${icon}` : key.substring(0, 2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { Component, inject, Input } from '@angular/core'
|
||||
import { Router, RouterLink } from '@angular/router'
|
||||
import {
|
||||
AboutModule,
|
||||
AdditionalModule,
|
||||
MarketplacePkg,
|
||||
PackageModule,
|
||||
} from '@start9labs/marketplace'
|
||||
import {
|
||||
Emver,
|
||||
ErrorService,
|
||||
LoadingService,
|
||||
SharedPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import { TuiLetModule } from '@taiga-ui/cdk'
|
||||
import { TuiAlertService } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { combineLatest, map } from 'rxjs'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ClientStorageService } from 'src/app/services/client-storage.service'
|
||||
|
||||
import { toDesktopItem } from '../../../utils/to-desktop-item'
|
||||
import { NavigationService } from '../../../services/navigation.service'
|
||||
import { SideloadDependenciesComponent } from './dependencies.component'
|
||||
|
||||
@Component({
|
||||
selector: 'sideload-package',
|
||||
template: `
|
||||
<ng-content></ng-content>
|
||||
<marketplace-package *tuiLet="button$ | async as button" [pkg]="package">
|
||||
<a
|
||||
*ngIf="button !== null && button !== 'Install'"
|
||||
tuiButton
|
||||
appearance="secondary"
|
||||
[routerLink]="'/portal/service/' + package.manifest.id"
|
||||
>
|
||||
View installed
|
||||
</a>
|
||||
<button *ngIf="button" tuiButton (click)="upload()">
|
||||
{{ button }}
|
||||
</button>
|
||||
</marketplace-package>
|
||||
<marketplace-about [pkg]="package"></marketplace-about>
|
||||
<sideload-dependencies
|
||||
*ngIf="!(package.manifest.dependencies | empty)"
|
||||
[package]="package"
|
||||
></sideload-dependencies>
|
||||
<marketplace-additional [pkg]="package"></marketplace-additional>
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterLink,
|
||||
SharedPipesModule,
|
||||
AboutModule,
|
||||
AdditionalModule,
|
||||
PackageModule,
|
||||
TuiButtonModule,
|
||||
TuiLetModule,
|
||||
SideloadDependenciesComponent,
|
||||
],
|
||||
})
|
||||
export class SideloadPackageComponent {
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly router = inject(Router)
|
||||
private readonly navigation = inject(NavigationService)
|
||||
private readonly alerts = inject(TuiAlertService)
|
||||
private readonly emver = inject(Emver)
|
||||
|
||||
readonly button$ = combineLatest([
|
||||
inject(ClientStorageService).showDevTools$,
|
||||
inject(PatchDB<DataModel>)
|
||||
.watch$('package-data')
|
||||
.pipe(
|
||||
map(local =>
|
||||
local[this.package.manifest.id]
|
||||
? this.emver.compare(
|
||||
local[this.package.manifest.id].manifest.version,
|
||||
this.package.manifest.version,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
]).pipe(
|
||||
map(([devtools, version]) => {
|
||||
switch (version) {
|
||||
case null:
|
||||
return 'Install'
|
||||
case 1:
|
||||
return 'Update'
|
||||
case -1:
|
||||
return devtools ? 'Downgrade' : ''
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
@Input({ required: true })
|
||||
package!: MarketplacePkg
|
||||
|
||||
@Input({ required: true })
|
||||
file!: File
|
||||
|
||||
async upload() {
|
||||
const loader = this.loader.open('Uploading package').subscribe()
|
||||
const { manifest, icon } = this.package
|
||||
const { size } = this.file
|
||||
|
||||
try {
|
||||
const pkg = await this.api.sideloadPackage({ manifest, icon, size })
|
||||
|
||||
await this.api.uploadPackage(pkg, this.file)
|
||||
await this.router.navigate(['/portal/service', manifest.id])
|
||||
|
||||
this.navigation.removeTab(toDesktopItem('/portal/system/sideload'))
|
||||
this.alerts
|
||||
.open('Package uploaded successfully', { status: 'success' })
|
||||
.subscribe()
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
} finally {
|
||||
loader.unsubscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { MarketplacePkg } from '@start9labs/marketplace'
|
||||
import { TuiLinkModule, TuiWrapperModule } from '@taiga-ui/core'
|
||||
import { TuiAvatarModule, TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TuiInputFilesModule,
|
||||
tuiInputFilesOptionsProvider,
|
||||
} from '@taiga-ui/kit'
|
||||
import { Subject } from 'rxjs'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
|
||||
import { parseS9pk, validateS9pk } from './sideload.utils'
|
||||
import { SideloadPackageComponent } from './package.component'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ng-container *ngIf="refresh$ | async"></ng-container>
|
||||
<sideload-package
|
||||
*ngIf="package && file; else upload"
|
||||
[package]="package"
|
||||
[file]="file"
|
||||
>
|
||||
<button
|
||||
tuiIconButton
|
||||
appearance="secondary"
|
||||
iconLeft="tuiIconXLarge"
|
||||
[style.border-radius.%]="100"
|
||||
[style.float]="'right'"
|
||||
(click)="clear()"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</sideload-package>
|
||||
<ng-template #upload>
|
||||
<tui-input-files
|
||||
[ngModel]="null"
|
||||
(ngModelChange)="onFile($event)"
|
||||
(click)="clear()"
|
||||
>
|
||||
<input tuiInputFiles accept=".s9pk" />
|
||||
<ng-template>
|
||||
<div *ngIf="invalid; else valid">
|
||||
<tui-avatar
|
||||
tuiWrapper
|
||||
appearance="secondary"
|
||||
src="tuiIconXCircleLarge"
|
||||
></tui-avatar>
|
||||
<p [style.color]="'var(--tui-negative)'">Invalid package file</p>
|
||||
<button tuiButton>Try again</button>
|
||||
</div>
|
||||
<ng-template #valid>
|
||||
<div>
|
||||
<tui-avatar
|
||||
tuiWrapper
|
||||
appearance="secondary"
|
||||
src="tuiIconUploadCloudLarge"
|
||||
></tui-avatar>
|
||||
<p>Upload .s9pk package file</p>
|
||||
<p *ngIf="isTor" [style.color]="'var(--tui-positive)'">
|
||||
Tip: switch to LAN for faster uploads
|
||||
</p>
|
||||
<button tuiButton>Upload</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</tui-input-files>
|
||||
</ng-template>
|
||||
`,
|
||||
host: { class: 'g-page', '[style.padding-top.rem]': '2' },
|
||||
styles: [
|
||||
`
|
||||
tui-input-files {
|
||||
height: 100%;
|
||||
max-width: 40rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [tuiInputFilesOptionsProvider({ maxFileSize: Infinity })],
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
TuiInputFilesModule,
|
||||
TuiLinkModule,
|
||||
TuiAvatarModule,
|
||||
TuiWrapperModule,
|
||||
TuiButtonModule,
|
||||
SideloadPackageComponent,
|
||||
],
|
||||
})
|
||||
export class SideloadComponent {
|
||||
readonly refresh$ = new Subject<void>()
|
||||
readonly isTor = inject(ConfigService).isTor()
|
||||
|
||||
invalid = false
|
||||
file: File | null = null
|
||||
package: MarketplacePkg | null = null
|
||||
|
||||
clear() {
|
||||
this.invalid = false
|
||||
this.file = null
|
||||
this.package = null
|
||||
}
|
||||
|
||||
async onFile(file: File | null) {
|
||||
if (!file || !(await validateS9pk(file))) {
|
||||
this.invalid = true
|
||||
} else {
|
||||
this.package = await parseS9pk(file)
|
||||
this.file = file
|
||||
}
|
||||
|
||||
this.refresh$.next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import { Manifest, MarketplacePkg } from '@start9labs/marketplace'
|
||||
import cbor from 'cbor'
|
||||
|
||||
interface Positions {
|
||||
[key: string]: [bigint, bigint] // [position, length]
|
||||
}
|
||||
|
||||
const MAGIC = new Uint8Array([59, 59])
|
||||
const VERSION = new Uint8Array([1])
|
||||
|
||||
export async function validateS9pk(file: File): Promise<boolean> {
|
||||
const magic = new Uint8Array(await blobToBuffer(file.slice(0, 2)))
|
||||
const version = new Uint8Array(await blobToBuffer(file.slice(2, 3)))
|
||||
|
||||
return compare(magic, MAGIC) && compare(version, VERSION)
|
||||
}
|
||||
|
||||
export async function parseS9pk(file: File): Promise<MarketplacePkg> {
|
||||
const positions: Positions = {}
|
||||
// magic=2bytes, version=1bytes, pubkey=32bytes, signature=64bytes, toc_length=4bytes = 103byte is starting point
|
||||
let start = 103
|
||||
let end = start + 1 // 104
|
||||
const tocLength = new DataView(
|
||||
await blobToBuffer(file.slice(99, 103) ?? new Blob()),
|
||||
).getUint32(0, false)
|
||||
await getPositions(start, end, file, positions, tocLength as any)
|
||||
|
||||
const manifest = await getAsset(positions, file, 'manifest')
|
||||
const [icon] = await Promise.all([
|
||||
await getIcon(positions, file, manifest),
|
||||
// getAsset(positions, file, 'license'),
|
||||
// getAsset(positions, file, 'instructions'),
|
||||
])
|
||||
|
||||
return {
|
||||
manifest,
|
||||
icon,
|
||||
license: '',
|
||||
instructions: '',
|
||||
categories: [],
|
||||
versions: [],
|
||||
'dependency-metadata': {},
|
||||
'published-at': '',
|
||||
}
|
||||
}
|
||||
|
||||
async function getPositions(
|
||||
initialStart: number,
|
||||
initialEnd: number,
|
||||
file: Blob,
|
||||
positions: Positions,
|
||||
tocLength: number,
|
||||
) {
|
||||
let start = initialStart
|
||||
let end = initialEnd
|
||||
const titleLength = new Uint8Array(
|
||||
await blobToBuffer(file.slice(start, end)),
|
||||
)[0]
|
||||
const tocTitle = await file.slice(end, end + titleLength).text()
|
||||
start = end + titleLength
|
||||
end = start + 8
|
||||
const chapterPosition = new DataView(
|
||||
await blobToBuffer(file.slice(start, end)),
|
||||
).getBigUint64(0, false)
|
||||
start = end
|
||||
end = start + 8
|
||||
const chapterLength = new DataView(
|
||||
await blobToBuffer(file.slice(start, end)),
|
||||
).getBigUint64(0, false)
|
||||
|
||||
positions[tocTitle] = [chapterPosition, chapterLength]
|
||||
start = end
|
||||
end = start + 1
|
||||
if (end <= tocLength + (initialStart - 1)) {
|
||||
await getPositions(start, end, file, positions, tocLength)
|
||||
}
|
||||
}
|
||||
|
||||
async function readBlobAsDataURL(
|
||||
f: Blob | File,
|
||||
): Promise<string | ArrayBuffer | null> {
|
||||
const reader = new FileReader()
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result)
|
||||
}
|
||||
reader.readAsDataURL(f)
|
||||
reader.onerror = _ => reject(new Error('error reading blob'))
|
||||
})
|
||||
}
|
||||
|
||||
async function blobToDataURL(data: Blob | File): Promise<string> {
|
||||
const res = await readBlobAsDataURL(data)
|
||||
if (res instanceof ArrayBuffer) {
|
||||
throw new Error('readBlobAsDataURL response should not be an array buffer')
|
||||
}
|
||||
if (res == null) {
|
||||
throw new Error('readBlobAsDataURL response should not be null')
|
||||
}
|
||||
if (typeof res === 'string') return res
|
||||
throw new Error('no possible blob to data url resolution found')
|
||||
}
|
||||
|
||||
async function blobToBuffer(data: Blob | File): Promise<ArrayBuffer> {
|
||||
const res = await readBlobToArrayBuffer(data)
|
||||
if (res instanceof String) {
|
||||
throw new Error('readBlobToArrayBuffer response should not be a string')
|
||||
}
|
||||
if (res == null) {
|
||||
throw new Error('readBlobToArrayBuffer response should not be null')
|
||||
}
|
||||
if (res instanceof ArrayBuffer) return res
|
||||
throw new Error('no possible blob to array buffer resolution found')
|
||||
}
|
||||
|
||||
async function readBlobToArrayBuffer(
|
||||
f: Blob | File,
|
||||
): Promise<string | ArrayBuffer | null> {
|
||||
const reader = new FileReader()
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result)
|
||||
}
|
||||
reader.readAsArrayBuffer(f)
|
||||
reader.onerror = _ => reject(new Error('error reading blob'))
|
||||
})
|
||||
}
|
||||
|
||||
function compare(a: Uint8Array, b: Uint8Array) {
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function getAsset(
|
||||
positions: Positions,
|
||||
file: Blob,
|
||||
asset: 'manifest' | 'license' | 'instructions',
|
||||
): Promise<any> {
|
||||
const data = await blobToBuffer(
|
||||
file.slice(
|
||||
Number(positions[asset][0]),
|
||||
Number(positions[asset][0]) + Number(positions[asset][1]),
|
||||
),
|
||||
)
|
||||
return cbor.decode(data, true)
|
||||
}
|
||||
|
||||
async function getIcon(
|
||||
positions: Positions,
|
||||
file: Blob,
|
||||
manifest: Manifest,
|
||||
): Promise<string> {
|
||||
const contentType = `image/${manifest.assets.icon.split('.').pop()}`
|
||||
const data = file.slice(
|
||||
Number(positions['icon'][0]),
|
||||
Number(positions['icon'][0]) + Number(positions['icon'][1]),
|
||||
contentType,
|
||||
)
|
||||
return blobToDataURL(data)
|
||||
}
|
||||
@@ -11,6 +11,13 @@ const ROUTES: Routes = [
|
||||
import('./backups/backups.component').then(m => m.BackupsComponent),
|
||||
data: toDesktopItem('/portal/system/backups'),
|
||||
},
|
||||
{
|
||||
title: systemTabResolver,
|
||||
path: 'sideload',
|
||||
loadComponent: () =>
|
||||
import('./sideload/sideload.component').then(m => m.SideloadComponent),
|
||||
data: toDesktopItem('/portal/system/sideload'),
|
||||
},
|
||||
{
|
||||
title: systemTabResolver,
|
||||
path: 'updates',
|
||||
|
||||
@@ -15,13 +15,12 @@ import {
|
||||
SharedPipesModule,
|
||||
} from '@start9labs/shared'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDialogService,
|
||||
TuiLinkModule,
|
||||
TuiLoaderModule,
|
||||
TuiSvgModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiAvatarModule } from '@taiga-ui/experimental'
|
||||
import { TuiAvatarModule, TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TUI_PROMPT,
|
||||
TuiAccordionModule,
|
||||
|
||||
@@ -12,8 +12,8 @@ export class NavigationService {
|
||||
return this.tabs
|
||||
}
|
||||
|
||||
removeTab(tab: NavigationItem) {
|
||||
this.tabs.next(this.tabs.value.filter(t => t !== tab))
|
||||
removeTab({ routerLink }: NavigationItem) {
|
||||
this.tabs.next(this.tabs.value.filter(t => t.routerLink !== routerLink))
|
||||
}
|
||||
|
||||
addTab(tab: NavigationItem) {
|
||||
|
||||
@@ -3,7 +3,8 @@ import { CommonModule } from '@angular/common'
|
||||
import { ReactiveFormsModule } from '@angular/forms'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { TuiValueChangesModule } from '@taiga-ui/cdk'
|
||||
import { TuiButtonModule, TuiModeModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiModeModule } from '@taiga-ui/core'
|
||||
import { FormModule } from 'src/app/common/form/form.module'
|
||||
import { FormPage } from './form.page'
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
title="Toggle masking"
|
||||
size="xs"
|
||||
class="button"
|
||||
[icon]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
[iconLeft]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
(click)="masked = !masked"
|
||||
></button>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { TuiButtonModule, TuiTextfieldControllerModule } from '@taiga-ui/core'
|
||||
import { TuiTextfieldControllerModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiInputModule } from '@taiga-ui/kit'
|
||||
import { TuiAutoFocusModule } from '@taiga-ui/cdk'
|
||||
import { PromptComponent } from './prompt.component'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { TuiButtonModule, TuiGroupModule } from '@taiga-ui/core'
|
||||
import { TuiGroupModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiCheckboxBlockModule } from '@taiga-ui/kit'
|
||||
import { BackupSelectPage } from './backup-select.page'
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { TuiButtonModule, TuiGroupModule } from '@taiga-ui/core'
|
||||
import { TuiGroupModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiCheckboxBlockModule } from '@taiga-ui/kit'
|
||||
import { RecoverSelectPage } from './recover-select.page'
|
||||
import { ToOptionsPipe } from './to-options.pipe'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { TuiButtonModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TargetSelectPage, TargetStatusComponent } from './target-select.page'
|
||||
import { TargetPipesModule } from '../../pipes/target-pipes.module'
|
||||
import { TextSpinnerComponentModule } from '@start9labs/shared'
|
||||
|
||||
@@ -3,11 +3,8 @@ import { NgModule } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiNotificationModule,
|
||||
TuiWrapperModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiNotificationModule, TuiWrapperModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiInputModule, TuiToggleModule } from '@taiga-ui/kit'
|
||||
import { BackupJobsPage } from './backup-jobs.page'
|
||||
import { EditJobComponent } from './edit-job/edit-job.component'
|
||||
|
||||
@@ -2,11 +2,11 @@ import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ReactiveFormsModule } from '@angular/forms'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiLoaderModule,
|
||||
TuiModeModule,
|
||||
TuiNotificationModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { FormPageModule } from 'src/app/apps/ui/modals/form/form.module'
|
||||
|
||||
import { AppConfigPage } from './app-config.page'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { TuiButtonModule, TuiNotificationModule } from '@taiga-ui/core'
|
||||
import { TuiNotificationModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { EmailPage } from './email.page'
|
||||
import { Routes, RouterModule } from '@angular/router'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
|
||||
@@ -4,13 +4,13 @@ import { FormsModule } from '@angular/forms'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiDataListModule,
|
||||
TuiHostedDropdownModule,
|
||||
TuiNotificationModule,
|
||||
TuiSvgModule,
|
||||
TuiWrapperModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiBadgeModule, TuiInputModule, TuiToggleModule } from '@taiga-ui/kit'
|
||||
import { ProxiesPage } from './proxies.page'
|
||||
|
||||
|
||||
@@ -102,11 +102,8 @@
|
||||
appearance="flat"
|
||||
tuiHostedDropdownHost
|
||||
size="s"
|
||||
[icon]="icon"
|
||||
iconLeft="tuiIconMoreHorizontal"
|
||||
></button>
|
||||
<ng-template #icon>
|
||||
<tui-svg src="tuiIconMoreHorizontal"></tui-svg>
|
||||
</ng-template>
|
||||
</tui-hosted-dropdown>
|
||||
<ng-template #dropdown let-close="close">
|
||||
<tui-data-list>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { MarkdownPipeModule, SafeLinksDirective } from '@start9labs/shared'
|
||||
import { TuiButtonModule, TuiScrollbarModule } from '@taiga-ui/core'
|
||||
import { TuiScrollbarModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { TuiAutoFocusModule } from '@taiga-ui/cdk'
|
||||
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
|
||||
import { OSUpdatePage } from './os-update.page'
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
tuiIconButton
|
||||
type="button"
|
||||
class="remove"
|
||||
icon="tuiIconTrash"
|
||||
iconLeft="tuiIconTrash"
|
||||
appearance="icon"
|
||||
size="m"
|
||||
title="Remove"
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<button
|
||||
tuiIconButton
|
||||
size="s"
|
||||
icon="tuiIconChevronDown"
|
||||
iconLeft="tuiIconChevronDown"
|
||||
type="button"
|
||||
shape="rounded"
|
||||
class="button"
|
||||
[class.button_open]="open"
|
||||
[style.border-radius.%]="100"
|
||||
[appearance]="invalid ? 'secondary-destructive' : 'secondary'"
|
||||
></button>
|
||||
<ng-content></ng-content>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
title="Generate"
|
||||
size="xs"
|
||||
class="button"
|
||||
icon="tuiIconRefreshCcw"
|
||||
iconLeft="tuiIconRefreshCcw"
|
||||
(click)="generate()"
|
||||
></button>
|
||||
<button
|
||||
@@ -38,7 +38,7 @@
|
||||
title="Toggle masking"
|
||||
size="xs"
|
||||
class="button"
|
||||
[icon]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
[iconLeft]="masked ? 'tuiIconEye' : 'tuiIconEyeOff'"
|
||||
(click)="masked = !masked"
|
||||
></button>
|
||||
</ng-template>
|
||||
|
||||
@@ -4,7 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { MaskitoModule } from '@maskito/angular'
|
||||
import { TuiMapperPipeModule, TuiValueChangesModule } from '@taiga-ui/cdk'
|
||||
import {
|
||||
TuiButtonModule,
|
||||
TuiErrorModule,
|
||||
TuiExpandModule,
|
||||
TuiHintModule,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
TuiTooltipModule,
|
||||
TuiWrapperModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import {
|
||||
TuiElasticContainerModule,
|
||||
TuiFieldErrorPipeModule,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { OSWelcomePage } from './os-welcome.page'
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -2,11 +2,8 @@ import { CommonModule } from '@angular/common'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule } from '@angular/router'
|
||||
import { TuiAutoFocusModule } from '@taiga-ui/cdk'
|
||||
import {
|
||||
TuiAlertModule,
|
||||
TuiButtonModule,
|
||||
TuiDialogModule,
|
||||
} from '@taiga-ui/core'
|
||||
import { TuiAlertModule, TuiDialogModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
|
||||
import { ToastContainerComponent } from './toast-container.component'
|
||||
import { NotificationsToastComponent } from './notifications-toast/notifications-toast.component'
|
||||
|
||||
@@ -375,6 +375,7 @@ ul {
|
||||
padding: 1px 2rem 3rem;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
isolation: isolate;
|
||||
|
||||
// TODO: Theme
|
||||
background: #373a3f;
|
||||
|
||||
Reference in New Issue
Block a user