mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-02 05:23:14 +00:00
refactor: completely remove ionic
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { AsyncPipe } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { RouterLink } from '@angular/router'
|
||||
import { TuiAlertModule } from '@taiga-ui/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { Observable, Subject, merge, pairwise, map, endWith } from 'rxjs'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'notifications-toast',
|
||||
template: `
|
||||
<ng-template
|
||||
[tuiAlert]="!!(visible$ | async)"
|
||||
[tuiAlertOptions]="{ label: 'StartOS' }"
|
||||
(tuiAlertChange)="onDismiss()"
|
||||
>
|
||||
New notifications
|
||||
<a routerLink="/notifications" [queryParams]="{ toast: true }">View</a>
|
||||
</ng-template>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiAlertModule, RouterLink, AsyncPipe],
|
||||
})
|
||||
export class NotificationsToastComponent {
|
||||
private readonly dismiss$ = new Subject<boolean>()
|
||||
|
||||
readonly visible$: Observable<boolean> = merge(
|
||||
this.dismiss$,
|
||||
inject(PatchDB<DataModel>)
|
||||
.watch$('serverInfo', 'unreadNotifications', 'count')
|
||||
.pipe(
|
||||
pairwise(),
|
||||
map(([prev, cur]) => cur > prev),
|
||||
endWith(false),
|
||||
),
|
||||
)
|
||||
|
||||
onDismiss() {
|
||||
this.dismiss$.next(false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { AsyncPipe } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { SwUpdate } from '@angular/service-worker'
|
||||
import { Emver, LoadingService } from '@start9labs/shared'
|
||||
import { TuiAutoFocusModule } from '@taiga-ui/cdk'
|
||||
import { TuiDialogModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { debounceTime, endWith, map, merge, Subject } from 'rxjs'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'refresh-alert',
|
||||
template: `
|
||||
<!-- <ng-template-->
|
||||
<!-- [tuiDialog]="show$ | async"-->
|
||||
<!-- [tuiDialogOptions]="{ label: 'Refresh Needed', size: 's' }"-->
|
||||
<!-- (tuiDialogChange)="onDismiss()"-->
|
||||
<!-- >-->
|
||||
<!-- Your user interface is cached and out of date. Hard refresh the page to-->
|
||||
<!-- get the latest UI.-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li>-->
|
||||
<!-- <b>On Mac</b>-->
|
||||
<!-- : cmd + shift + R-->
|
||||
<!-- </li>-->
|
||||
<!-- <li>-->
|
||||
<!-- <b>On Linux/Windows</b>-->
|
||||
<!-- : ctrl + shift + R-->
|
||||
<!-- </li>-->
|
||||
<!-- </ul>-->
|
||||
<!-- <button-->
|
||||
<!-- tuiButton-->
|
||||
<!-- tuiAutoFocus-->
|
||||
<!-- appearance="secondary"-->
|
||||
<!-- style="float: right"-->
|
||||
<!-- (click)="onDismiss()"-->
|
||||
<!-- >-->
|
||||
<!-- Ok-->
|
||||
<!-- </button>-->
|
||||
<!-- </ng-template>-->
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiDialogModule, AsyncPipe, TuiButtonModule, TuiAutoFocusModule],
|
||||
})
|
||||
export class RefreshAlertComponent {
|
||||
private readonly updates = inject(SwUpdate)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly emver = inject(Emver)
|
||||
private readonly config = inject(ConfigService)
|
||||
private readonly dismiss$ = new Subject<boolean>()
|
||||
|
||||
readonly show$ = merge(
|
||||
this.dismiss$,
|
||||
inject(PatchDB<DataModel>)
|
||||
.watch$('serverInfo', 'version')
|
||||
.pipe(
|
||||
map(version => !!this.emver.compare(this.config.version, version)),
|
||||
endWith(false),
|
||||
),
|
||||
).pipe(debounceTime(0))
|
||||
|
||||
// @TODO use this like we did on 0344
|
||||
onPwa = false
|
||||
|
||||
ngOnInit() {
|
||||
this.onPwa = window.matchMedia('(display-mode: standalone)').matches
|
||||
}
|
||||
|
||||
async pwaReload() {
|
||||
const loader = this.loader.open('Reloading PWA...').subscribe()
|
||||
|
||||
try {
|
||||
// attempt to update to the latest client version available
|
||||
await this.updates.activateUpdate()
|
||||
} catch (e) {
|
||||
console.error('Error activating update from service worker: ', e)
|
||||
} finally {
|
||||
loader.unsubscribe()
|
||||
// always reload, as this resolves most out of sync cases
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
onDismiss() {
|
||||
this.dismiss$.next(false)
|
||||
}
|
||||
}
|
||||
86
web/projects/ui/src/app/components/report.component.ts
Normal file
86
web/projects/ui/src/app/components/report.component.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { TuiDialogContext, TuiSvgModule } from '@taiga-ui/core'
|
||||
import {
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
PolymorpheusComponent,
|
||||
} from '@tinkoff/ng-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">
|
||||
<strong>System data</strong>
|
||||
<div [style.color]="system.color">{{ system.result }}</div>
|
||||
</div>
|
||||
<tui-svg [src]="system.icon" [style.color]="system.color"></tui-svg>
|
||||
</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' }}
|
||||
</div>
|
||||
</div>
|
||||
<tui-svg
|
||||
[src]="getIcon(pkg.value.error)"
|
||||
[style.color]="getColor(pkg.value.error)"
|
||||
></tui-svg>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [CommonModule, TuiSvgModule],
|
||||
})
|
||||
export class BackupsReportModal {
|
||||
private readonly context =
|
||||
inject<TuiDialogContext<void, { report: BackupReport; timestamp: string }>>(
|
||||
POLYMORPHEUS_CONTEXT,
|
||||
)
|
||||
|
||||
readonly system = this.getSystem()
|
||||
|
||||
get report(): BackupReport {
|
||||
return this.context.data.report
|
||||
}
|
||||
|
||||
get timestamp(): string {
|
||||
return this.context.data.timestamp
|
||||
}
|
||||
|
||||
getColor(error: unknown) {
|
||||
return error ? 'var(--tui-negative)' : 'var(--tui-positive)'
|
||||
}
|
||||
|
||||
getIcon(error: unknown) {
|
||||
return error ? 'tuiIconMinusCircleLarge' : 'tuiIconCheckLarge'
|
||||
}
|
||||
|
||||
private getSystem() {
|
||||
if (!this.report.server.attempted) {
|
||||
return {
|
||||
result: 'Not Attempted',
|
||||
icon: 'tuiIconMinusLarge',
|
||||
color: 'var(--tui-text-02)',
|
||||
}
|
||||
}
|
||||
|
||||
if (this.report.server.error) {
|
||||
return {
|
||||
result: `Failed: ${this.report.server.error}`,
|
||||
icon: 'tuiIconMinusCircleLarge',
|
||||
color: 'var(--tui-negative)',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
result: 'Succeeded',
|
||||
icon: 'tuiIconCheckLarge',
|
||||
color: 'var(--tui-positive)',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const REPORT = new PolymorpheusComponent(BackupsReportModal)
|
||||
35
web/projects/ui/src/app/components/sidebar-host.component.ts
Normal file
35
web/projects/ui/src/app/components/sidebar-host.component.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Directive,
|
||||
Injectable,
|
||||
} from '@angular/core'
|
||||
import {
|
||||
AbstractTuiPortalHostComponent,
|
||||
AbstractTuiPortalService,
|
||||
TuiDropdownPortalService,
|
||||
} from '@taiga-ui/cdk'
|
||||
|
||||
@Injectable({ providedIn: `root` })
|
||||
export class SidebarService extends AbstractTuiPortalService {}
|
||||
|
||||
@Directive({
|
||||
selector: '[tuiSidebar]',
|
||||
standalone: true,
|
||||
providers: [
|
||||
{ provide: TuiDropdownPortalService, useExisting: SidebarService },
|
||||
],
|
||||
})
|
||||
export class SidebarDirective {}
|
||||
|
||||
@Component({
|
||||
selector: 'sidebar-host',
|
||||
template: '<ng-container #viewContainer></ng-container>',
|
||||
styles: [':host { position: fixed; top: 0; }'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
providers: [
|
||||
{ provide: AbstractTuiPortalService, useExisting: SidebarService },
|
||||
],
|
||||
})
|
||||
export class SidebarHostComponent extends AbstractTuiPortalHostComponent {}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'svg-definitions',
|
||||
template: `
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="bevel-light">
|
||||
<feFlood flood-color="white" flood-opacity="0.1" />
|
||||
<feComposite in2="SourceAlpha" operator="out" />
|
||||
<feGaussianBlur stdDeviation="0.5" result="blur" />
|
||||
<feOffset dx="-1" dy="1" />
|
||||
<feComposite operator="atop" in2="SourceGraphic" />
|
||||
</filter>
|
||||
<filter id="bevel-dark">
|
||||
<feFlood flood-color="black" flood-opacity="0.3" />
|
||||
<feComposite in2="SourceAlpha" operator="out" />
|
||||
<feGaussianBlur stdDeviation="0.5" result="blur" />
|
||||
<feOffset dx="1" dy="-1" />
|
||||
<feComposite operator="atop" in2="SourceGraphic" />
|
||||
</filter>
|
||||
<filter id="round-corners">
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur" />
|
||||
<feColorMatrix
|
||||
in="blur"
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9"
|
||||
result="flt_tag"
|
||||
/>
|
||||
<feComposite in="SourceGraphic" in2="flt_tag" operator="atop" />
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
`,
|
||||
styles: `
|
||||
:host {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SvgDefinitionsComponent {}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { NotificationsToastComponent } from './notifications-toast.component'
|
||||
import { RefreshAlertComponent } from './refresh-alert.component'
|
||||
import { UpdateToastComponent } from './update-toast.component'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'toast-container',
|
||||
template: `
|
||||
<notifications-toast />
|
||||
<refresh-alert />
|
||||
<update-toast />
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
NotificationsToastComponent,
|
||||
UpdateToastComponent,
|
||||
RefreshAlertComponent,
|
||||
],
|
||||
})
|
||||
export class ToastContainerComponent {}
|
||||
79
web/projects/ui/src/app/components/update-toast.component.ts
Normal file
79
web/projects/ui/src/app/components/update-toast.component.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { AsyncPipe } from '@angular/common'
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||
import { ErrorService, LoadingService } from '@start9labs/shared'
|
||||
import { TuiAlertModule } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
endWith,
|
||||
filter,
|
||||
merge,
|
||||
Observable,
|
||||
Subject,
|
||||
} from 'rxjs'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'update-toast',
|
||||
template: `
|
||||
<ng-template
|
||||
[tuiAlert]="!!(visible$ | async)"
|
||||
[tuiAlertOptions]="{
|
||||
label: 'StartOS download complete!',
|
||||
status: 'success',
|
||||
autoClose: false
|
||||
}"
|
||||
(tuiAlertChange)="onDismiss()"
|
||||
>
|
||||
Restart your server for these updates to take effect. It can take several
|
||||
minutes to come back online.
|
||||
<div>
|
||||
<button
|
||||
tuiButton
|
||||
appearance="secondary"
|
||||
size="s"
|
||||
style="margin-top: 8px"
|
||||
(click)="restart()"
|
||||
>
|
||||
Restart
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [TuiButtonModule, TuiAlertModule, AsyncPipe],
|
||||
})
|
||||
export class UpdateToastComponent {
|
||||
private readonly api = inject(ApiService)
|
||||
private readonly errorService = inject(ErrorService)
|
||||
private readonly loader = inject(LoadingService)
|
||||
private readonly dismiss$ = new Subject<boolean>()
|
||||
|
||||
readonly visible$: Observable<boolean> = merge(
|
||||
this.dismiss$,
|
||||
inject(PatchDB<DataModel>)
|
||||
.watch$('serverInfo', 'statusInfo', 'updated')
|
||||
.pipe(distinctUntilChanged(), filter(Boolean), endWith(false)),
|
||||
)
|
||||
|
||||
onDismiss() {
|
||||
this.dismiss$.next(false)
|
||||
}
|
||||
|
||||
async restart(): Promise<void> {
|
||||
this.onDismiss()
|
||||
|
||||
const loader = this.loader.open('Restarting...').subscribe()
|
||||
|
||||
try {
|
||||
await this.api.restartServer({})
|
||||
} catch (e: any) {
|
||||
await this.errorService.handleError(e)
|
||||
} finally {
|
||||
await loader.unsubscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
49
web/projects/ui/src/app/components/welcome.component.ts
Normal file
49
web/projects/ui/src/app/components/welcome.component.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Component, inject, Inject } from '@angular/core'
|
||||
import { TuiDialogContext } from '@taiga-ui/core'
|
||||
import { TuiButtonModule } from '@taiga-ui/experimental'
|
||||
import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus'
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
template: `
|
||||
<h3>
|
||||
This release:
|
||||
<em>0.3.5</em>
|
||||
</h3>
|
||||
<p>
|
||||
View the complete
|
||||
<a
|
||||
href="https://github.com/Start9Labs/start-os/releases/tag/v0.3.5"
|
||||
target="_blank"
|
||||
noreferrer
|
||||
>
|
||||
release notes
|
||||
</a>
|
||||
for more details.
|
||||
</p>
|
||||
<h5>Highlights</h5>
|
||||
<ul class="spaced-list">
|
||||
<li>
|
||||
This release contains significant under-the-hood improvements to
|
||||
performance and reliability
|
||||
</li>
|
||||
<li>Ditch Docker, replace with Podman</li>
|
||||
<li>Remove locking behavior from PatchDB and optimize</li>
|
||||
<li>Boost efficiency of service manager</li>
|
||||
<li>Require HTTPS on LAN, and improve setup flow for trusting Root CA</li>
|
||||
<li>Better default privacy settings for Firefox kiosk mode</li>
|
||||
<li>Eliminate memory leak from Javascript runtime</li>
|
||||
<li>Other small bug fixes</li>
|
||||
<li>Update license to MIT</li>
|
||||
</ul>
|
||||
|
||||
<p [style.text-align]="'center'">
|
||||
<button tuiButton (click)="context.$implicit.complete()">Begin</button>
|
||||
</p>
|
||||
`,
|
||||
styles: 'li { margin-bottom: 0.5rem }',
|
||||
imports: [TuiButtonModule],
|
||||
})
|
||||
export class WelcomeComponent {
|
||||
readonly context = inject<TuiDialogContext>(POLYMORPHEUS_CONTEXT)
|
||||
}
|
||||
Reference in New Issue
Block a user