chore: comments (#2863)

This commit is contained in:
Alex Inkin
2025-04-06 17:18:01 +04:00
committed by GitHub
parent f51dcf23d6
commit 31856d9895
45 changed files with 362 additions and 503 deletions

View File

@@ -1,6 +1,6 @@
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { Exver, MarkdownPipeModule } from '@start9labs/shared'
import { Exver, MarkdownPipe } from '@start9labs/shared'
import { TuiButton, TuiDialogContext, TuiLoader } from '@taiga-ui/core'
import { TuiAccordion } from '@taiga-ui/kit'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
@@ -21,13 +21,7 @@ import { MarketplacePkg } from '../../src/types'
</tui-accordion>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
TuiButton,
TuiLoader,
TuiAccordion,
MarkdownPipeModule,
],
imports: [CommonModule, TuiButton, TuiLoader, TuiAccordion, MarkdownPipe],
})
export class ReleaseNotesComponent {
private readonly exver = inject(Exver)

View File

@@ -5,7 +5,7 @@ import { NgModule } from '@angular/core'
import { RouterModule } from '@angular/router'
import { AboutComponent } from './about.component'
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
import { MarkdownPipeModule, SafeLinksDirective } from '@start9labs/shared'
import { MarkdownPipe, SafeLinksDirective } from '@start9labs/shared'
@NgModule({
imports: [
@@ -14,7 +14,7 @@ import { MarkdownPipeModule, SafeLinksDirective } from '@start9labs/shared'
TuiTagModule,
NgDompurifyModule,
SafeLinksDirective,
MarkdownPipeModule,
MarkdownPipe,
TuiButton,
],
declarations: [AboutComponent],

View File

@@ -1,14 +1,13 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { ActivatedRoute } from '@angular/router'
import {
getErrorMessage,
MarkdownPipeModule,
SafeLinksDirective,
} from '@start9labs/shared'
import { TuiLoader, TuiNotification } from '@taiga-ui/core'
import { ActivatedRoute, Data } from '@angular/router'
import { TuiDialogContext, TuiLoader, TuiNotification } from '@taiga-ui/core'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
import { catchError, ignoreElements, of } from 'rxjs'
import { SafeLinksDirective } from '../directives/safe-links.directive'
import { MarkdownPipe } from '../pipes/markdown.pipe'
import { getErrorMessage } from '../services/error.service'
@Component({
template: `
@@ -21,7 +20,7 @@ import { catchError, ignoreElements, of } from 'rxjs'
@if (content(); as result) {
<div safeLinks [innerHTML]="result | markdown | dompurify"></div>
} @else {
<tui-loader textContent="Loading" />
<tui-loader textContent="Loading" [style.height.%]="100" />
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -30,13 +29,15 @@ import { catchError, ignoreElements, of } from 'rxjs'
imports: [
TuiNotification,
TuiLoader,
MarkdownPipeModule,
NgDompurifyModule,
MarkdownPipe,
SafeLinksDirective,
],
})
export default class ServiceMarkdownRoute {
private readonly data = inject(ActivatedRoute).snapshot.data
export class MarkdownComponent {
private readonly data =
injectContext<TuiDialogContext<void, Data>>({ optional: true })?.data ||
inject(ActivatedRoute).snapshot.data
readonly content = toSignal<string>(this.data['content'])
readonly error = toSignal(
@@ -46,3 +47,5 @@ export default class ServiceMarkdownRoute {
),
)
}
export const MARKDOWN = new PolymorpheusComponent(MarkdownComponent)

View File

@@ -1,18 +0,0 @@
<tui-notification
*ngIf="error$ | async as error"
appearance="negative"
safeLinks
>
{{ error }}
</tui-notification>
<div
*ngIf="content$ | async as result; else loading"
safeLinks
class="content-padding"
[innerHTML]="result | markdown | dompurify"
></div>
<ng-template #loading>
<tui-loader [textContent]="'Loading ' + title | titlecase" />
</ng-template>

View File

@@ -1,22 +0,0 @@
import { CommonModule } from '@angular/common'
import { NgModule } from '@angular/core'
import { TuiLoader, TuiNotification } from '@taiga-ui/core'
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
import { SafeLinksDirective } from '../../directives/safe-links.directive'
import { MarkdownPipeModule } from '../../pipes/markdown/markdown.module'
import { MarkdownComponent } from './markdown.component'
@NgModule({
declarations: [MarkdownComponent],
imports: [
CommonModule,
MarkdownPipeModule,
SafeLinksDirective,
NgDompurifyModule,
TuiLoader,
TuiNotification,
],
exports: [MarkdownComponent],
})
export class MarkdownModule {}

View File

@@ -1,18 +0,0 @@
.content-padding {
padding: 0 16px 16px 16px;
}
:host ::ng-deep img {
border-radius: 0 !important;
}
:host ::ng-deep h1,
:host ::ng-deep h2,
:host ::ng-deep h3,
:host ::ng-deep h4,
:host ::ng-deep h5,
:host ::ng-deep h6,
:host ::ng-deep hr,
:host ::ng-deep p {
margin: revert;
}

View File

@@ -1,49 +0,0 @@
import { Component, Inject } from '@angular/core'
import { TuiDialogContext } from '@taiga-ui/core'
import {
POLYMORPHEUS_CONTEXT,
PolymorpheusComponent,
} from '@taiga-ui/polymorpheus'
import {
catchError,
ignoreElements,
share,
defer,
isObservable,
Observable,
of,
} from 'rxjs'
import { getErrorMessage } from '../../services/error.service'
@Component({
selector: 'markdown',
templateUrl: './markdown.component.html',
styleUrls: ['./markdown.component.scss'],
})
export class MarkdownComponent {
readonly content$ = defer(() =>
isObservable(this.context.data.content)
? this.context.data.content
: of(this.context.data.content),
).pipe(share())
readonly error$ = this.content$.pipe(
ignoreElements(),
catchError(e => of(getErrorMessage(e))),
)
constructor(
@Inject(POLYMORPHEUS_CONTEXT)
private readonly context: TuiDialogContext<
void,
{ content: string | Observable<string> }
>,
) {}
get title(): string {
return this.context.label || ''
}
}
export const MARKDOWN = new PolymorpheusComponent(MarkdownComponent)

View File

@@ -2,6 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core'
import { marked } from 'marked'
@Pipe({
standalone: true,
name: 'markdown',
})
export class MarkdownPipe implements PipeTransform {

View File

@@ -1,8 +0,0 @@
import { NgModule } from '@angular/core'
import { MarkdownPipe } from './markdown.pipe'
@NgModule({
declarations: [MarkdownPipe],
exports: [MarkdownPipe],
})
export class MarkdownPipeModule {}

View File

@@ -10,11 +10,10 @@ export * from './components/initializing/initializing.component'
export * from './components/loading/loading.component'
export * from './components/loading/loading.component'
export * from './components/loading/loading.service'
export * from './components/markdown/markdown.component'
export * from './components/markdown/markdown.component.module'
export * from './components/ticker/ticker.component'
export * from './components/ticker/ticker.module'
export * from './components/drive.component'
export * from './components/markdown.component'
export * from './components/server.component'
export * from './directives/drag-scroller.directive'
@@ -22,14 +21,13 @@ export * from './directives/safe-links.directive'
export * from './pipes/exver/exver.module'
export * from './pipes/exver/exver.pipe'
export * from './pipes/markdown/markdown.module'
export * from './pipes/markdown/markdown.pipe'
export * from './pipes/shared/shared.module'
export * from './pipes/shared/empty.pipe'
export * from './pipes/shared/includes.pipe'
export * from './pipes/shared/trust.pipe'
export * from './pipes/unit-conversion/unit-conversion.module'
export * from './pipes/unit-conversion/unit-conversion.pipe'
export * from './pipes/markdown.pipe'
export * from './services/copy.service'
export * from './services/download-html.service'

View File

@@ -1,55 +1,47 @@
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { TuiDialogContext, TuiIcon } from '@taiga-ui/core'
import {
POLYMORPHEUS_CONTEXT,
PolymorpheusComponent,
} from '@taiga-ui/polymorpheus'
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { TuiDialogContext, TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { BackupReport } from 'src/app/services/api/api.types'
@Component({
template: `
<h3 class="g-title">Completed: {{ timestamp | date: 'medium' }}</h3>
<div class="g-action">
<div [style.flex]="1">
<h3 class="g-title">Completed: {{ data.createdAt | date: 'medium' }}</h3>
<div tuiCell>
<div tuiTitle>
<strong>System data</strong>
<div [style.color]="system.color">{{ system.result }}</div>
<div tuiSubtitle [style.color]="system.color">{{ system.result }}</div>
</div>
<tui-icon [icon]="system.icon" [style.color]="system.color" />
</div>
<div *ngFor="let pkg of report?.packages | keyvalue" class="g-action">
<div [style.flex]="1">
<strong>{{ pkg.key }}</strong>
<div [style.color]="getColor(pkg.value.error)">
{{ pkg.value.error ? 'Failed: ' + pkg.value.error : 'Succeeded' }}
@for (pkg of data.content.packages | keyvalue; track $index) {
<div tuiCell>
<div tuiTitle>
<strong>{{ pkg.key }}</strong>
<div tuiSubtitle [style.color]="getColor(pkg.value.error)">
{{ pkg.value.error ? 'Failed: ' + pkg.value.error : 'Succeeded' }}
</div>
</div>
<tui-icon
[icon]="getIcon(pkg.value.error)"
[style.color]="getColor(pkg.value.error)"
/>
</div>
<tui-icon
[icon]="getIcon(pkg.value.error)"
[style.color]="getColor(pkg.value.error)"
/>
</div>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [CommonModule, TuiIcon],
imports: [CommonModule, TuiIcon, TuiCell, TuiTitle],
})
export class BackupsReportModal {
private readonly context =
inject<
TuiDialogContext<void, { content: BackupReport; timestamp: string }>
>(POLYMORPHEUS_CONTEXT)
readonly data =
injectContext<
TuiDialogContext<void, { content: BackupReport; createdAt: string }>
>().data
readonly system = this.getSystem()
get report(): BackupReport {
return this.context.data.content
}
get timestamp(): string {
return this.context.data.timestamp
}
getColor(error: unknown) {
return error ? 'var(--tui-text-negative)' : 'var(--tui-text-positive)'
}
@@ -59,7 +51,7 @@ export class BackupsReportModal {
}
private getSystem() {
if (!this.report.server.attempted) {
if (!this.data.content.server.attempted) {
return {
result: 'Not Attempted',
icon: '@tui.minus',
@@ -67,9 +59,9 @@ export class BackupsReportModal {
}
}
if (this.report.server.error) {
if (this.data.content.server.error) {
return {
result: `Failed: ${this.report.server.error}`,
result: `Failed: ${this.data.content.server.error}`,
icon: '@tui.circle-minus',
color: 'var(--tui-text-negative)',
}

View File

@@ -44,11 +44,6 @@ export default {
check: 'Check for updates',
},
},
sync: {
title: 'Clock sync failure',
subtitle:
'This will cause connectivity issues. To resolve it, refer to the',
},
},
},
}

View File

@@ -46,11 +46,6 @@ export default {
check: 'Buscar actualizaciones',
},
},
sync: {
title: 'Fallo en la sincronización del reloj',
subtitle:
'Esto causará problemas de conectividad. Para resolverlo, consulta la',
},
},
},
} satisfies i18n

View File

@@ -6,13 +6,9 @@ import {
viewChild,
ViewContainerRef,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { PatchDB } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { TitleService } from 'src/app/services/title.service'
import { HeaderMenuComponent } from './menu.component'
import { HeaderNavigationComponent } from './navigation.component'
import { HeaderSnekDirective } from './snek.directive'
import { HeaderStatusComponent } from './status.component'
@Component({
@@ -21,12 +17,6 @@ import { HeaderStatusComponent } from './status.component'
<header-navigation />
<div class="item item_center">
<div class="mobile"><ng-container #vcr /></div>
<img
[appSnek]="snekScore()"
class="snek"
alt="Play Snake"
src="assets/img/icons/snek.png"
/>
</div>
<header-status class="item item_connection" />
<header-menu class="item item_corner" />
@@ -105,19 +95,6 @@ import { HeaderStatusComponent } from './status.component'
}
}
.snek {
@include center-top();
@include transition(opacity);
right: 2rem;
width: 1rem;
opacity: 0.2;
cursor: pointer;
&:hover {
opacity: 1;
}
}
:host-context(tui-root._mobile) {
.item_center::before {
left: -2rem;
@@ -144,7 +121,6 @@ import { HeaderStatusComponent } from './status.component'
imports: [
HeaderStatusComponent,
HeaderNavigationComponent,
HeaderSnekDirective,
HeaderMenuComponent,
],
})
@@ -152,15 +128,6 @@ export class HeaderComponent implements OnInit {
private readonly title = inject(TitleService)
readonly vcr = viewChild.required('vcr', { read: ViewContainerRef })
readonly snekScore = toSignal(
inject<PatchDB<DataModel>>(PatchDB).watch$(
'ui',
'gaming',
'snake',
'highScore',
),
{ initialValue: 0 },
)
ngOnInit() {
this.title.register(this.vcr())

View File

@@ -63,7 +63,7 @@ type ClearnetForm = {
</ng-template>
<button
tuiButton
appearance="accent"
[appearance]="isPublic() ? 'primary-destructive' : 'accent'"
[iconStart]="isPublic() ? '@tui.globe-lock' : '@tui.globe'"
[style.margin-inline-start]="'auto'"
(click)="toggle()"

View File

@@ -6,8 +6,20 @@ import { tuiInjectElement } from '@taiga-ui/cdk'
selector: '[appUptime]',
template: '',
styles: `
:host {
&::before {
content: 'Uptime: ';
}
&:empty::after {
content: '-';
}
}
:host-context(tui-root._mobile) {
display: none;
grid-row: 2;
margin: 0;
font: var(--tui-font-text-ui-s);
}
`,
})
@@ -22,7 +34,7 @@ export class UptimeComponent implements OnChanges, OnDestroy {
clearInterval(this.interval)
if (!this.appUptime) {
this.el.textContent = '-'
this.el.textContent = ''
} else {
this.el.textContent = uptime(new Date(this.appUptime))
this.interval = setInterval(() => {

View File

@@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { TuiDialogService, TuiIcon } from '@taiga-ui/core'
import { TuiDialogService, TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { BackupsUpcomingComponent } from './components/upcoming.component'
import { HISTORY } from './modals/history.component'
import { JOBS } from './modals/jobs.component'
@@ -12,12 +13,12 @@ import { BackupsRestoreService } from './services/restore.service'
<section>
<h3 class="g-title">Options</h3>
@for (option of options; track $index) {
<button class="g-action" (click)="option.action()">
<button tuiCell (click)="option.action()">
<tui-icon [icon]="option.icon" />
<div>
<span tuiTitle>
<strong>{{ option.name }}</strong>
<div>{{ option.description }}</div>
</div>
<span tuiSubtitle>{{ option.description }}</span>
</span>
</button>
}
</section>
@@ -27,7 +28,7 @@ import { BackupsRestoreService } from './services/restore.service'
host: { class: 'g-page' },
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [BackupsUpcomingComponent, TuiIcon],
imports: [BackupsUpcomingComponent, TuiIcon, TuiCell, TuiTitle],
})
export default class BackupsComponent {
private readonly dialogs = inject(TuiDialogService)

View File

@@ -31,10 +31,8 @@ interface Package {
@if (pkgs) {
@for (pkg of pkgs; track $index) {
<label tuiBlock>
<div class="g-action">
<img class="icon" alt="" [src]="pkg.icon" />
{{ pkg.title }}
</div>
<img class="icon" alt="" [src]="pkg.icon" />
{{ pkg.title }}
<input
type="checkbox"
tuiCheckbox

View File

@@ -228,7 +228,7 @@ export class BackupsHistoryModal {
label: 'Backup Report',
data: {
content: report,
timestamp: completedAt,
createdAt: completedAt,
},
})
.subscribe()

View File

@@ -14,7 +14,9 @@ import {
TuiDialogService,
TuiIcon,
TuiLoader,
TuiTitle,
} from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { BackupTarget } from 'src/app/services/api/api.types'
@@ -34,25 +36,22 @@ import { TARGETS } from './targets.component'
<h3 class="g-title">Saved Targets</h3>
@for (target of targets | keyvalue; track $index) {
<button
class="g-action"
tuiCell
[disabled]="isDisabled(target.value)"
(click)="select(target.value, target.key)"
>
@if (target.value | getDisplayInfo; as displayInfo) {
<tui-icon [icon]="displayInfo.icon" />
<div>
<span tuiTitle>
<strong>{{ displayInfo.name }}</strong>
<backups-status
[type]="context.data.type"
[mountable]="target.value.mountable"
[hasBackup]="hasBackup(target.value)"
/>
<div [style.color]="'var(--tui-text-secondary'">
{{ displayInfo.description }}
<br />
{{ displayInfo.path }}
</div>
</div>
<span tuiSubtitle>{{ displayInfo.description }}</span>
<span tuiSubtitle>{{ displayInfo.path }}</span>
</span>
}
</button>
} @empty {
@@ -70,6 +69,8 @@ import { TARGETS } from './targets.component'
BackupsStatusComponent,
GetDisplayInfoPipe,
KeyValuePipe,
TuiCell,
TuiTitle,
],
})
export class BackupsTargetModal {

View File

@@ -32,7 +32,7 @@ const LABELS = {
.cpu {
position: relative;
margin: 1rem auto;
width: 7rem;
width: 8rem;
aspect-ratio: 1;
}

View File

@@ -24,7 +24,7 @@ const LABELS = {
selector: 'metrics-memory',
template: `
<label tuiProgressLabel>
<tui-progress-circle size="l" [max]="100" [value]="used()" />
<tui-progress-circle size="xl" [max]="100" [value]="used()" />
{{ value()?.percentageUsed?.value | value }}%
</label>
<metrics-data [labels]="labels" [value]="value()" />

View File

@@ -30,7 +30,7 @@ const LABELS = {
progress {
height: 1.5rem;
width: 80%;
margin: 3.75rem auto;
margin: 4.25rem auto;
border-radius: 0;
clip-path: none;
mask: linear-gradient(to right, #000 80%, transparent 80%);

View File

@@ -56,6 +56,7 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core'
:host {
height: 100%;
min-height: 7.5rem;
display: flex;
flex-direction: column;
align-items: center;

View File

@@ -1,7 +1,14 @@
import { DatePipe } from '@angular/common'
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { TuiNotification, TuiTitle } from '@taiga-ui/core'
import {
TuiHint,
TuiIcon,
TuiLink,
TuiNotification,
TuiTitle,
} from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { TimeService } from 'src/app/services/time.service'
@Component({
@@ -11,18 +18,45 @@ import { TimeService } from 'src/app/services/time.service'
@if (now(); as time) {
@if (!time.synced) {
<tui-notification appearance="warning">
NTP not synced, time could be wrong
<ng-container *ngTemplateOutlet="hint" />
</tui-notification>
}
<div tuiTitle>
<div tuiSubtitle class="g-secondary">
{{ time.now | date: 'h:mm a z' : 'UTC' }}
<div tuiCell>
<div tuiTitle [style.text-align]="'center'">
<div tuiSubtitle class="g-secondary">
{{ time.now | date: 'h:mm a z' : 'UTC' }}
</div>
<b>{{ time.now | date: 'MMMM d, y' : 'UTC' }}</b>
</div>
<b>{{ time.now | date: 'MMMM d, y' : 'UTC' }}</b>
@if (!time.synced) {
<tui-icon
icon="@tui.circle-alert"
class="g-warning"
[tuiHint]="hint"
/>
}
</div>
} @else {
Loading...
}
<ng-template #hint>
<div tuiTitle>
Clock sync failure
<div tuiSubtitle>
To resolve it, refer to
<a
tuiLink
iconEnd="@tui.external-link"
appearance=""
href="https://docs.start9.com/0.3.5.x/support/common-issues#clock-sync-failure"
target="_blank"
rel="noreferrer"
[pseudo]="true"
[textContent]="'the docs'"
></a>
</div>
</div>
</ng-template>
`,
styles: `
:host {
@@ -32,14 +66,39 @@ import { TimeService } from 'src/app/services/time.service'
justify-content: center;
gap: 1rem;
margin-bottom: 1.5rem;
[tuiCell],
[tuiTitle],
[tuiSubtitle] {
margin: 0;
justify-content: center;
}
}
[tuiTitle] {
text-align: center;
tui-icon {
display: none;
}
:host-context(tui-root._mobile) {
tui-notification {
display: none;
}
tui-icon {
display: block;
}
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiNotification, DatePipe, TuiTitle],
imports: [
CommonModule,
TuiNotification,
TuiTitle,
TuiLink,
TuiCell,
TuiIcon,
TuiHint,
],
})
export class TimeComponent {
readonly now = toSignal(inject(TimeService).now$)

View File

@@ -19,7 +19,7 @@ import { getManifest } from 'src/app/utils/get-package-data'
@if (['running', 'starting', 'restarting'].includes(status)) {
<button
tuiButton
appearance="outline-destructive"
appearance="secondary-destructive"
iconStart="@tui.square"
(click)="actions.stop(manifest)"
>
@@ -30,7 +30,6 @@ import { getManifest } from 'src/app/utils/get-package-data'
@if (status === 'running') {
<button
tuiButton
appearance="outline"
iconStart="@tui.rotate-cw"
(click)="actions.restart(manifest)"
>
@@ -41,7 +40,6 @@ import { getManifest } from 'src/app/utils/get-package-data'
@if (status === 'stopped') {
<button
tuiButton
appearance="outline"
iconStart="@tui.play"
(click)="actions.start(manifest, hasUnmet(dependencies))"
>
@@ -52,19 +50,21 @@ import { getManifest } from 'src/app/utils/get-package-data'
styles: [
`
:host {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
inline-size: 20rem;
max-inline-size: 100%;
margin-block-start: 1rem;
&:nth-child(3) {
grid-row: span 2;
}
}
:host-context(tui-root._mobile) {
display: flex;
margin: 0;
inline-size: min-content;
[tuiButton] {
font-size: 0;

View File

@@ -5,13 +5,10 @@ import {
Input,
} from '@angular/core'
import { RouterLink } from '@angular/router'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { TuiButton, TuiLink } from '@taiga-ui/core'
import { TuiBadge } from '@taiga-ui/kit'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { getManifest } from '../../../../../utils/get-package-data'
import { MappedInterface } from '../types/mapped-interface'
@Component({
@@ -30,25 +27,21 @@ import { MappedInterface } from '../types/mapped-interface'
</td>
<td>
@if (info.public) {
<button
tuiButton
size="s"
<a
class="hosting"
tuiLink
iconStart="@tui.globe"
appearance="positive"
(click)="toggle()"
>
Public
</button>
[textContent]="'Public'"
></a>
} @else {
<button
tuiButton
size="s"
<a
class="hosting"
tuiLink
iconStart="@tui.lock"
appearance="negative"
(click)="toggle()"
>
Private
</button>
[textContent]="'Private'"
></a>
}
</td>
<td [style.grid-area]="'span 2'">
@@ -70,6 +63,24 @@ import { MappedInterface } from '../types/mapped-interface'
</td>
`,
styles: `
@import '@taiga-ui/core/styles/taiga-ui-local';
:host {
cursor: pointer;
clip-path: inset(0 round var(--tui-radius-m));
@include transition(background);
}
[tuiLink] {
background: transparent;
}
@media ($tui-mouse) {
:host:hover {
background: var(--tui-background-neutral-1);
}
}
strong {
white-space: nowrap;
}
@@ -88,6 +99,10 @@ import { MappedInterface } from '../types/mapped-interface'
td {
padding: 0;
}
.hosting {
font-size: 0;
}
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -96,9 +111,6 @@ import { MappedInterface } from '../types/mapped-interface'
})
export class ServiceInterfaceComponent {
private readonly config = inject(ConfigService)
private readonly errorService = inject(ErrorService)
private readonly loader = inject(LoadingService)
private readonly api = inject(ApiService)
@Input({ required: true })
info!: MappedInterface
@@ -125,31 +137,4 @@ export class ServiceInterfaceComponent {
? 'null'
: this.config.launchableAddress(this.info, this.pkg.hosts)
}
async toggle() {
const loader = this.loader
.open(`Making ${this.info.public ? 'private' : 'public'}`)
.subscribe()
const params = {
internalPort: this.info.addressInfo.internalPort,
public: !this.info.public,
}
try {
if (!this.info.public) {
await this.api.pkgBindingSetPubic({
...params,
host: this.info.addressInfo.hostId,
package: getManifest(this.pkg).id,
})
} else {
await this.api.serverBindingSetPubic(params)
}
} catch (e: any) {
this.errorService.handleError(e)
} finally {
loader.unsubscribe()
}
}
}

View File

@@ -5,6 +5,7 @@ import {
inject,
input,
} from '@angular/core'
import { RouterLink } from '@angular/router'
import { TuiTable } from '@taiga-ui/addon-table'
import { tuiDefaultSort } from '@taiga-ui/cdk'
import { ConfigService } from 'src/app/services/config.service'
@@ -31,6 +32,7 @@ import { ServiceInterfaceComponent } from './interface.component'
@for (info of interfaces(); track $index) {
<tr
serviceInterface
[routerLink]="info.routerLink"
[info]="info"
[pkg]="pkg()"
[disabled]="disabled()"
@@ -46,7 +48,7 @@ import { ServiceInterfaceComponent } from './interface.component'
`,
host: { class: 'g-card' },
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ServiceInterfaceComponent, TuiTable],
imports: [ServiceInterfaceComponent, TuiTable, RouterLink],
})
export class ServiceInterfacesComponent {
private readonly config = inject(ConfigService)

View File

@@ -70,8 +70,8 @@ import { InstallingProgressDisplayPipe } from '../pipes/install-progress.pipe'
:host-context(tui-root._mobile) {
div {
flex-direction: row;
justify-content: space-between;
display: grid;
grid-template-columns: 1fr max-content;
padding: 0.5rem 0;
}

View File

@@ -27,12 +27,16 @@ import { StatusComponent } from './status.component'
<a [routerLink]="routerLink">{{ manifest.title }}</a>
</td>
<td [style.grid-area]="'2 / 2'">{{ manifest.version }}</td>
<td [appUptime]="$any(pkg.status).started"></td>
<td
[style.grid-area]="'3 / 2'"
[appUptime]="$any(pkg.status).started"
[style.grid-column]="2"
[style.grid-row]="4"
></td>
<td
appStatus
[pkg]="pkg"
[hasDepErrors]="hasError(depErrors)"
[style.grid-area]="'3 / 2'"
></td>
<td [style.grid-area]="'2 / 3'" [style.text-align]="'center'">
<fieldset
@@ -56,6 +60,10 @@ import { StatusComponent } from './status.component'
}
}
td::before {
display: none;
}
img {
display: block;
height: 2rem;
@@ -75,7 +83,7 @@ import { StatusComponent } from './status.component'
:host-context(tui-root._mobile) {
position: relative;
display: grid;
grid-template: 2rem 2rem 2rem/6rem 1fr 2rem;
grid-template: 1.25rem 1.75rem 1.5rem 1.25rem/6rem 1fr 2rem;
align-items: center;
padding: 1rem;
@@ -90,6 +98,15 @@ import { StatusComponent } from './status.component'
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--tui-text-secondary);
&::before {
display: inline;
}
&:empty {
display: none;
}
}
}
`,

View File

@@ -5,7 +5,7 @@ import {
INJECTOR,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { CopyService, getPkgId } from '@start9labs/shared'
import { CopyService, getPkgId, MarkdownComponent } from '@start9labs/shared'
import { TuiDialogService } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
@@ -17,7 +17,6 @@ import {
FALLBACK_URL,
ServiceAdditionalItemComponent,
} from '../components/additional-item.component'
import ServiceMarkdownRoute from './markdown.component'
@Component({
template: `
@@ -52,7 +51,7 @@ import ServiceMarkdownRoute from './markdown.component'
export default class ServiceAboutRoute {
private readonly copyService = inject(CopyService)
private readonly markdown = inject(TuiDialogService).open(
new PolymorpheusComponent(ServiceMarkdownRoute, inject(INJECTOR)),
new PolymorpheusComponent(MarkdownComponent, inject(INJECTOR)),
{ label: 'License', size: 'l' },
)

View File

@@ -1,3 +1,4 @@
import { NgTemplateOutlet } from '@angular/common'
import {
ChangeDetectionStrategy,
Component,
@@ -10,7 +11,7 @@ import { RouterLink } from '@angular/router'
import { getPkgId } from '@start9labs/shared'
import { TuiItem } from '@taiga-ui/cdk'
import { TuiButton, TuiLink } from '@taiga-ui/core'
import { TuiBreadcrumbs } from '@taiga-ui/kit'
import { TuiBadge, TuiBreadcrumbs } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component'
import { getAddresses } from 'src/app/routes/portal/components/interfaces/interface.utils'
@@ -23,12 +24,16 @@ import { TitleDirective } from 'src/app/services/title.service'
<ng-container *title>
<a routerLink="../.." tuiIconButton iconStart="@tui.arrow-left">Back</a>
{{ interface()?.name }}
<ng-container *ngTemplateOutlet="badge" />
</ng-container>
<tui-breadcrumbs size="l" [style.margin-block-end.rem]="1">
<a *tuiItem tuiLink appearance="action-grayscale" routerLink="../..">
Dashboard
</a>
<span *tuiItem class="g-primary">{{ interface()?.name }}</span>
<span *tuiItem class="g-primary">
{{ interface()?.name }}
<ng-container *ngTemplateOutlet="badge" />
</span>
</tui-breadcrumbs>
@if (interface(); as serviceInterface) {
<app-interface
@@ -36,6 +41,16 @@ import { TitleDirective } from 'src/app/services/title.service'
[serviceInterface]="serviceInterface"
/>
}
<ng-template #badge>
<tui-badge
[iconStart]="interface()?.public ? '@tui.globe' : '@tui.lock'"
[style.vertical-align.rem]="-0.125"
[style.margin]="'0 0.25rem -0.25rem'"
[appearance]="interface()?.public ? 'positive' : 'negative'"
>
{{ interface()?.public ? 'Public' : 'Private' }}
</tui-badge>
</ng-template>
`,
styles: `
:host-context(tui-root._mobile) tui-breadcrumbs {
@@ -53,6 +68,8 @@ import { TitleDirective } from 'src/app/services/title.service'
TuiBreadcrumbs,
TuiItem,
TuiLink,
TuiBadge,
NgTemplateOutlet,
],
})
export default class ServiceInterfaceRoute {

View File

@@ -1,5 +1,6 @@
import { inject } from '@angular/core'
import { ActivatedRouteSnapshot, ResolveFn, Routes } from '@angular/router'
import { MarkdownComponent } from '@start9labs/shared'
import { defer, map, Observable, of } from 'rxjs'
import { share } from 'rxjs/operators'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -22,7 +23,7 @@ export const ROUTES: Routes = [
},
{
path: 'instructions',
loadComponent: () => import('./routes/markdown.component'),
component: MarkdownComponent,
resolve: { content: getStatic('instructions.md') },
canActivate: [
({ paramMap }: ActivatedRouteSnapshot) => {

View File

@@ -38,7 +38,7 @@ import { ConfigService } from 'src/app/services/config.service'
import { EOSService } from 'src/app/services/eos.service'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { TitleDirective } from 'src/app/services/title.service'
import { SystemSyncComponent } from './sync.component'
import { SnekDirective } from './snek.directive'
import { UPDATE } from './update.component'
import { SystemWipeComponent } from './wipe.component'
@@ -61,9 +61,6 @@ import { SystemWipeComponent } from './wipe.component'
</hgroup>
</header>
@if (server(); as server) {
@if (!server.ntpSynced) {
<system-sync />
}
<div tuiCell tuiAppearance="outline-grayscale">
<tui-icon icon="@tui.zap" />
<span tuiTitle>
@@ -75,6 +72,7 @@ import { SystemWipeComponent } from './wipe.component'
<button
tuiButton
appearance="accent"
iconStart="@tui.refresh-cw"
[disabled]="eos.updatingOrBackingUp$ | async"
(click)="onUpdate()"
>
@@ -125,7 +123,7 @@ import { SystemWipeComponent } from './wipe.component'
</button>
</div>
<div tuiCell tuiAppearance="outline-grayscale">
<tui-icon icon="@tui.download" />
<tui-icon icon="@tui.award" />
<span tuiTitle>
<strong>
{{ 'system.general.ca.title' | i18n }}
@@ -134,7 +132,7 @@ import { SystemWipeComponent } from './wipe.component'
{{ 'system.general.ca.subtitle' | i18n }}
</span>
</span>
<button tuiButton (click)="downloadCA()">
<button tuiButton iconStart="@tui.download" (click)="downloadCA()">
{{ 'system.general.ca.button' | i18n }}
</button>
</div>
@@ -168,6 +166,12 @@ import { SystemWipeComponent } from './wipe.component'
</button>
</div>
}
<img
[snek]="score()"
class="snek"
alt="Play Snake"
src="assets/img/icons/snek.png"
/>
}
<!-- hidden element for downloading cert -->
<a id="download-ca" href="/static/local-root-ca.crt"></a>
@@ -177,6 +181,16 @@ import { SystemWipeComponent } from './wipe.component'
max-inline-size: 40rem;
}
.snek {
width: 1rem;
opacity: 0.2;
cursor: pointer;
&:hover {
opacity: 1;
}
}
strong {
line-height: 1.25rem;
}
@@ -205,12 +219,12 @@ import { SystemWipeComponent } from './wipe.component'
TuiButton,
TuiIcon,
TitleDirective,
SystemSyncComponent,
TuiButtonLoading,
TuiButtonSelect,
TuiDataListWrapper,
TuiTextfield,
FormsModule,
SnekDirective,
],
})
export default class SystemGeneralComponent {
@@ -235,6 +249,10 @@ export default class SystemGeneralComponent {
readonly eos = inject(EOSService)
readonly i18n = inject(i18nService)
readonly languages = ['english', 'spanish']
readonly score = toSignal(
this.patch.watch$('ui', 'gaming', 'snake', 'highScore'),
{ initialValue: 0 },
)
onUpdate() {
if (this.server()?.statusInfo.updated) {

View File

@@ -42,7 +42,7 @@ import { injectContext } from '@taiga-ui/polymorpheus'
],
imports: [TuiButton],
})
export class HeaderSnekComponent implements AfterViewInit, OnDestroy {
export class SnekComponent implements AfterViewInit, OnDestroy {
private readonly document = inject(DOCUMENT)
private readonly dialog = injectContext<TuiDialogContext<number, number>>()

View File

@@ -4,31 +4,31 @@ import { TuiDialogService } from '@taiga-ui/core'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { HeaderSnekComponent } from './snek.component'
import { SnekComponent } from './snek.component'
@Directive({
standalone: true,
selector: 'img[appSnek]',
selector: 'img[snek]',
})
export class HeaderSnekDirective {
export class SnekDirective {
private readonly dialogs = inject(TuiDialogService)
private readonly loader = inject(LoadingService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
@Input()
appSnek = 0
snek = 0
@HostListener('click')
async onClick() {
this.dialogs
.open<number>(new PolymorpheusComponent(HeaderSnekComponent), {
.open<number>(new PolymorpheusComponent(SnekComponent), {
label: 'Snake!',
closeable: false,
dismissible: false,
data: this.appSnek,
data: this.snek,
})
.pipe(filter(score => score > this.appSnek))
.pipe(filter(score => score > this.snek))
.subscribe(async score => {
const loader = this.loader.open('Saving high score...').subscribe()

View File

@@ -1,29 +0,0 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { TuiLink, TuiNotification, TuiTitle } from '@taiga-ui/core'
import { i18nPipe } from 'src/app/i18n/i18n.pipe'
@Component({
selector: 'system-sync',
template: `
<tui-notification appearance="warning">
<div tuiTitle>
{{ 'system.general.sync.title' | i18n }}
<div tuiSubtitle>
{{ 'system.general.sync.subtitle' | i18n }}
<a
tuiLink
iconEnd="@tui.external-link"
href="https://docs.start9.com/0.3.5.x/support/common-issues#clock-sync-failure"
target="_blank"
rel="noreferrer"
[textContent]="'StartOS docs'"
></a>
</div>
</div>
</tui-notification>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [TuiNotification, TuiTitle, TuiLink, i18nPipe],
})
export class SystemSyncComponent {}

View File

@@ -4,7 +4,7 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
import {
ErrorService,
LoadingService,
MarkdownPipeModule,
MarkdownPipe,
SafeLinksDirective,
} from '@start9labs/shared'
import {
@@ -36,7 +36,7 @@ import { EOSService } from 'src/app/services/eos.service'
standalone: true,
imports: [
CommonModule,
MarkdownPipeModule,
MarkdownPipe,
NgDompurifyModule,
SafeLinksDirective,
TuiAutoFocus,

View File

@@ -39,7 +39,7 @@ import { SessionsTableComponent } from './table.component'
<button
tuiButton
size="xs"
appearance="negative"
appearance="primary-destructive"
[style.margin-inline-start]="'auto'"
[disabled]="!selected.length"
(click)="terminate(selected, others || [])"
@@ -60,7 +60,6 @@ import { SessionsTableComponent } from './table.component'
TuiLet,
RouterLink,
TitleDirective,
TuiTable,
TuiHeader,
TuiTitle,
],

View File

@@ -87,15 +87,19 @@ import { PlatformInfoPipe } from './platform-info.pipe'
grid-template-columns: 2.5rem 1fr;
&:has(:checked) .platform {
color: var(--tui-text-action);
visibility: hidden;
}
}
input {
@include fullsize();
z-index: 1;
opacity: 0;
transform: none;
left: 0.25rem;
&:not(:checked) {
@include fullsize();
z-index: 1;
visibility: hidden;
transform: none;
}
}
td {

View File

@@ -8,19 +8,14 @@ import {
} from '@angular/core'
import { RouterLink } from '@angular/router'
import { MarketplacePkg } from '@start9labs/marketplace'
import { MarkdownPipeModule, SafeLinksDirective } from '@start9labs/shared'
import { MarkdownPipe, SafeLinksDirective } from '@start9labs/shared'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import {
TuiButton,
TuiIcon,
TuiLink,
TuiLoader,
TuiTitle,
} from '@taiga-ui/core'
import { TuiButton, TuiIcon, TuiLink, TuiTitle } from '@taiga-ui/core'
import { TuiExpand } from '@taiga-ui/experimental'
import {
TUI_CONFIRM,
TuiAvatar,
TuiButtonLoading,
TuiChevron,
TuiFade,
TuiProgressCircle,
@@ -84,20 +79,22 @@ import UpdatesComponent from './updates.component'
"
/>
} @else {
@if (ready()) {
<button
tuiButton
iconStart="@tui.arrow-big-up-dash"
[appearance]="error() ? 'destructive' : 'primary'"
(click.stop)="onClick()"
>
{{ error() ? 'Retry' : 'Update' }}
</button>
} @else {
<tui-loader [style.width.rem]="2" [inheritColor]="true" />
}
<button
tuiButton
size="s"
[loading]="!ready()"
[appearance]="error() ? 'destructive' : 'primary'"
(click.stop)="onClick()"
>
{{ error() ? 'Retry' : 'Update' }}
</button>
}
<button tuiIconButton appearance="icon" [tuiChevron]="expanded()">
<button
tuiIconButton
size="s"
appearance="icon"
[tuiChevron]="expanded()"
>
Show more
</button>
</div>
@@ -119,15 +116,16 @@ import UpdatesComponent from './updates.component'
</p>
<p tuiTitle>
<span>
<b>What's new</b>
(
<a
tuiLink
iconEnd="@tui.external-link"
routerLink="/portal/marketplace"
[queryParams]="{ url: parent.current()?.url, id: item().id }"
>
View listing
</a>
<b>What's new</b>
[textContent]="'View listing'"
></a>
)
</span>
</p>
<p
@@ -139,8 +137,6 @@ import UpdatesComponent from './updates.component'
</tr>
`,
styles: `
@import '@taiga-ui/core/styles/taiga-ui-local';
:host {
display: contents;
}
@@ -161,13 +157,6 @@ import UpdatesComponent from './updates.component'
word-break: break-word;
clip-path: inset(0 round var(--tui-radius-s));
cursor: pointer;
@include transition(background);
@media ($tui-mouse) {
&:hover {
background: var(--tui-background-neutral-1);
}
}
}
td {
@@ -190,10 +179,6 @@ import UpdatesComponent from './updates.component'
&[colspan]:only-child {
padding: 0 3rem;
text-align: left;
[tuiLink] {
float: right;
}
}
}
@@ -217,11 +202,6 @@ import UpdatesComponent from './updates.component'
padding: 0 0.5rem;
}
[tuiButton] {
font-size: 0;
gap: 0;
}
.desktop {
display: none;
}
@@ -236,19 +216,19 @@ import UpdatesComponent from './updates.component'
RouterLink,
TuiExpand,
TuiButton,
TuiButtonLoading,
TuiChevron,
TuiAvatar,
TuiLink,
TuiIcon,
TuiLoader,
TuiProgressCircle,
TuiTitle,
MarkdownPipeModule,
TuiFade,
MarkdownPipe,
NgDompurifyModule,
SafeLinksDirective,
DatePipe,
InstallingProgressPipe,
TuiFade,
],
})
export class UpdatesItemComponent {

View File

@@ -22,6 +22,7 @@ import {
import { TuiCell } from '@taiga-ui/layout'
import { PatchDB } from 'patch-db-client'
import { combineLatest, map, tap } from 'rxjs'
import { TableComponent } from 'src/app/routes/portal/components/table.component'
import { ConfigService } from 'src/app/services/config.service'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import {
@@ -91,43 +92,35 @@ interface UpdatesData {
Request Failed
</tui-notification>
}
<section class="g-card" [style.padding]="'0 1rem 1rem'">
<table tuiTable class="g-table">
<thead>
<tr>
<th tuiTh>Name</th>
<th tuiTh>Version</th>
<th tuiTh>Package Hash</th>
<th tuiTh>Published</th>
<th tuiTh></th>
</tr>
</thead>
<tbody>
@if (
data()?.marketplace?.[current()?.url || '']?.packages;
as packages
) {
@if (packages | filterUpdates: data()?.localPkgs; as updates) {
@for (pkg of updates; track $index) {
<updates-item
[item]="pkg"
[local]="data()?.localPkgs?.[pkg.id]!"
/>
} @empty {
<tr>
<td colspan="5">All services are up to date!</td>
</tr>
}
<section class="g-card">
<header>{{ current()?.name }}</header>
<table
[appTable]="['Name', 'Version', 'Package Hash', 'Published', '']"
>
@if (
data()?.marketplace?.[current()?.url || '']?.packages;
as packages
) {
@if (packages | filterUpdates: data()?.localPkgs; as updates) {
@for (pkg of updates; track $index) {
<updates-item
[item]="pkg"
[local]="data()?.localPkgs?.[pkg.id]!"
/>
} @empty {
<tr>
<td colspan="5">All services are up to date!</td>
</tr>
}
} @else {
<tr>
<td colspan="5" [tuiSkeleton]="true">Loading</td>
</tr>
<tr>
<td colspan="5" [tuiSkeleton]="true">Loading</td>
</tr>
}
</tbody>
} @else {
<tr>
<td colspan="5" [tuiSkeleton]="true">Loading</td>
</tr>
<tr>
<td colspan="5" [tuiSkeleton]="true">Loading</td>
</tr>
}
</table>
</section>
</section>
@@ -172,6 +165,11 @@ interface UpdatesData {
section {
background: none;
box-shadow: none;
padding: 0.5rem;
}
header {
display: none;
}
[tuiCell] {
@@ -203,7 +201,6 @@ interface UpdatesData {
TuiTitle,
TuiNotification,
TuiSkeleton,
TuiTable,
TuiBadgeNotification,
TuiFade,
TuiButton,
@@ -211,6 +208,7 @@ interface UpdatesData {
FilterUpdatesPipe,
UpdatesItemComponent,
TitleDirective,
TableComponent,
],
})
export default class UpdatesComponent {

View File

@@ -27,10 +27,12 @@ export class BadgeService {
private readonly notifications = inject(NotificationService)
private readonly exver = inject(Exver)
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly system$ = combineLatest([
this.patch.watch$('serverInfo', 'ntpSynced'),
inject(EOSService).updateAvailable$,
]).pipe(map(([synced, update]) => Number(!synced) + Number(update)))
private readonly system$ = inject(EOSService).updateAvailable$.pipe(
map(Number),
)
private readonly metrics$ = this.patch
.watch$('serverInfo', 'ntpSynced')
.pipe(map(synced => Number(!synced)))
private readonly marketplaceService = inject(MarketplaceService)
private readonly local$ = inject(ConnectionService).pipe(
@@ -86,6 +88,8 @@ export class BadgeService {
return this.updates$
case '/portal/system':
return this.system$
case '/portal/metrics':
return this.metrics$
case '/portal/notifications':
return this.notifications.unreadCount$
default:

View File

@@ -2,7 +2,7 @@ import { inject, Injectable } from '@angular/core'
import { ErrorService, MARKDOWN } from '@start9labs/shared'
import { TuiDialogService } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom, merge, shareReplay, Subject } from 'rxjs'
import { firstValueFrom, merge, of, shareReplay, Subject } from 'rxjs'
import { REPORT } from 'src/app/components/report.component'
import {
ServerNotification,
@@ -94,13 +94,14 @@ export class NotificationService {
full = false,
) {
const label = full || code === 2 ? title : 'Backup Report'
const content = code === 1 ? REPORT : MARKDOWN
const component = code === 1 ? REPORT : MARKDOWN
const content = code === 1 ? data : of(data)
this.dialogs
.open(full ? message : content, {
.open(full ? message : component, {
label,
data: {
content: data,
content,
timestamp: createdAt,
},
})

View File

@@ -147,11 +147,6 @@ hr {
> table[tuiTable] {
margin: 0 -0.5rem;
td:empty,
th:empty {
display: none;
}
}
> header {
@@ -183,6 +178,7 @@ hr {
border-radius: var(--tui-radius-s);
overflow: hidden;
box-shadow: inset 0 0 0 1px var(--tui-background-neutral-1);
clip-path: inset(0 round var(--tui-radius-s));
td,
th {
@@ -301,41 +297,6 @@ hr {
margin-top: 24px;
}
.g-action {
@include transition(background);
@include button-clear();
display: flex;
align-items: center;
width: stretch;
gap: 1rem;
text-align: left;
font-size: 0.85rem;
padding: 0.5rem 1rem;
margin: 0 -1rem;
line-height: 1.25rem;
border-radius: 0.5rem;
color: var(--tui-text-primary);
}
a.g-action,
button.g-action {
cursor: pointer;
&:disabled {
pointer-events: none;
opacity: var(--tui-disabled-opacity);
}
&:hover {
background: var(--tui-background-neutral-1);
}
&:not(:last-child) {
box-shadow: 0 calc(0.5rem + 1px) 0 -0.5rem var(--tui-background-neutral-1);
}
}
.g-toggle {
height: var(--tui-height-l);
display: flex;