Skip to content

frontend fixes for alpha.2 #2919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: next/major
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 107 additions & 107 deletions web/package-lock.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@
"@noble/hashes": "^1.4.0",
"@start9labs/argon2": "^0.2.2",
"@start9labs/start-sdk": "file:../sdk/baseDist",
"@taiga-ui/addon-charts": "4.32.0",
"@taiga-ui/addon-commerce": "4.32.0",
"@taiga-ui/addon-mobile": "4.32.0",
"@taiga-ui/addon-table": "4.32.0",
"@taiga-ui/cdk": "4.32.0",
"@taiga-ui/core": "4.32.0",
"@taiga-ui/addon-charts": "4.36.0",
"@taiga-ui/addon-commerce": "4.36.0",
"@taiga-ui/addon-mobile": "4.36.0",
"@taiga-ui/addon-table": "4.36.0",
"@taiga-ui/cdk": "4.36.0",
"@taiga-ui/core": "4.36.0",
"@taiga-ui/event-plugins": "4.5.1",
"@taiga-ui/experimental": "4.32.0",
"@taiga-ui/icons": "4.32.0",
"@taiga-ui/kit": "4.32.0",
"@taiga-ui/layout": "4.32.0",
"@taiga-ui/legacy": "4.32.0",
"@taiga-ui/experimental": "4.36.0",
"@taiga-ui/icons": "4.36.0",
"@taiga-ui/kit": "4.36.0",
"@taiga-ui/layout": "4.36.0",
"@taiga-ui/legacy": "4.36.0",
"@taiga-ui/polymorpheus": "4.9.0",
"@tinkoff/ng-dompurify": "4.0.0",
"ansi-to-html": "^0.7.2",
Expand Down
4 changes: 3 additions & 1 deletion web/projects/shared/src/i18n/dictionaries/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export default {
438: 'Kein Internet',
439: 'Verbindung wird hergestellt',
440: 'Fährt herunter',
441: 'Versionen',
441: 'Alle versionen',
442: 'Neue Benachrichtigungen',
443: 'Anzeigen',
444: 'PWA wird neu geladen',
Expand Down Expand Up @@ -498,4 +498,6 @@ export default {
495: 'Validierung',
496: 'in Bearbeitung',
497: 'abgeschlossen',
498: 'Klicken Sie hier, um alle Versionen anzuzeigen',
499: 'Keine anderen Versionen',
} satisfies i18n
6 changes: 4 additions & 2 deletions web/projects/shared/src/i18n/dictionaries/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ export const ENGLISH = {
'Donation Link': 280,
'Standard Actions': 281,
'Rebuild Service': 282, // as in, rebuild a software container
'Rebuilds the service container. Only necessary in there is a bug in StartOS': 283,
'Rebuilds the service container. Only necessary if there is a bug in StartOS': 283,
'Uninstall': 284,
'Uninstalls this service from StartOS and delete all data permanently.': 285,
'Dashboard': 286,
Expand Down Expand Up @@ -440,7 +440,7 @@ export const ENGLISH = {
'No Internet': 438,
'Connecting': 439,
'Shutting down': 440,
'Versions': 441,
'All versions': 441,
'New notifications': 442,
'View': 443,
'Reloading PWA': 444,
Expand Down Expand Up @@ -497,4 +497,6 @@ export const ENGLISH = {
'Validating': 495,
'in progress': 496,
'complete': 497,
'Click to view all versions': 498,
'No other versions': 499,
} as const
4 changes: 3 additions & 1 deletion web/projects/shared/src/i18n/dictionaries/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export default {
438: 'Sin Internet',
439: 'Conectando',
440: 'Apagando',
441: 'Versiones',
441: 'Todas las versiones',
442: 'Nuevas notificaciones',
443: 'Ver',
444: 'Recargando PWA',
Expand Down Expand Up @@ -498,4 +498,6 @@ export default {
495: 'Validando',
496: 'en progreso',
497: 'completo',
498: 'Haga clic para ver todas las versiones',
499: 'No hay otras versiones',
} satisfies i18n
4 changes: 3 additions & 1 deletion web/projects/shared/src/i18n/dictionaries/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export default {
438: 'Brak Internetu',
439: 'Łączenie',
440: 'Wyłączanie',
441: 'Wersje',
441: 'Wszystkie wersje',
442: 'Nowe powiadomienia',
443: 'Zobacz',
444: 'Przeładowywanie PWA',
Expand Down Expand Up @@ -498,4 +498,6 @@ export default {
495: 'Weryfikowanie',
496: 'w toku',
497: 'zakończono',
498: 'Kliknij, aby zobaczyć wszystkie wersje',
499: 'Brak innych wersji',
} satisfies i18n
3 changes: 1 addition & 2 deletions web/projects/shared/src/services/dialog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
TuiResponsiveDialogOptions,
TuiResponsiveDialogService,
} from '@taiga-ui/addon-mobile'
import { TuiAlertOptions } from '@taiga-ui/core'
import { TUI_CONFIRM, TuiConfirmData } from '@taiga-ui/kit'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PROMPT, PromptOptions } from '../components/prompt.component'
Expand Down Expand Up @@ -63,7 +62,7 @@ export class DialogService {

openAlert<T = void>(
message: i18nKey | undefined,
options: Partial<TuiAlertOptions<any>> & {
options: Partial<TuiResponsiveDialogOptions<any>> & {
label?: i18nKey
} = {},
) {
Expand Down
7 changes: 2 additions & 5 deletions web/projects/ui/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Component, inject } from '@angular/core'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { Title } from '@angular/platform-browser'
import { i18nService } from '@start9labs/shared'
import { PatchDB } from 'patch-db-client'
import { merge } from 'rxjs'
Expand Down Expand Up @@ -29,7 +28,6 @@ import { PatchMonitorService } from './services/patch-monitor.service'
`,
})
export class AppComponent {
private readonly title = inject(Title)
private readonly i18n = inject(i18nService)

readonly subscription = merge(
Expand All @@ -40,10 +38,9 @@ export class AppComponent {
.subscribe()

readonly ui = inject<PatchDB<DataModel>>(PatchDB)
.watch$('ui')
.watch$('ui', 'language')
.pipe(takeUntilDestroyed())
.subscribe(({ name, language }) => {
this.title.setTitle(name || 'StartOS')
.subscribe(language => {
this.i18n.setLanguage(language || 'english')
})
}
4 changes: 4 additions & 0 deletions web/projects/ui/src/app/app.providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import {
TUI_DATE_FORMAT,
TUI_DIALOGS_CLOSE,
tuiAlertOptionsProvider,
tuiButtonOptionsProvider,
tuiDropdownOptionsProvider,
tuiNumberFormatProvider,
Expand Down Expand Up @@ -58,6 +59,9 @@ export const APP_PROVIDERS: Provider[] = [
tuiButtonOptionsProvider({ size: 'm' }),
tuiTextfieldOptionsProvider({ hintOnDisabled: true }),
tuiDropdownOptionsProvider({ appearance: 'start-os' }),
tuiAlertOptionsProvider({
autoClose: appearance => (appearance === 'negative' ? 0 : 3000),
}),
{
provide: TUI_DATE_FORMAT,
useValue: of({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { AbstractTuiNullableControl } from '@taiga-ui/legacy'
import { filter } from 'rxjs'
import { TuiDialogContext } from '@taiga-ui/core'
import { TuiAlertService, TuiDialogContext } from '@taiga-ui/core'
import { IST } from '@start9labs/start-sdk'
import { ERRORS } from '../form-group/form-group.component'
import { FORM_CONTROL_PROVIDERS } from './form-control.providers'
import { DialogService, i18nKey } from '@start9labs/shared'
import { DialogService, i18nKey, i18nPipe } from '@start9labs/shared'

@Component({
selector: 'form-control',
Expand All @@ -26,6 +26,9 @@ export class FormControlComponent<
T extends Exclude<IST.ValueSpec, IST.ValueSpecHidden>,
V,
> extends AbstractTuiNullableControl<V> {
private readonly alerts = inject(TuiAlertService)
private readonly i18n = inject(i18nPipe)

@Input({ required: true })
spec!: T

Expand All @@ -35,7 +38,6 @@ export class FormControlComponent<
warned = false
focused = false
readonly order = ERRORS
private readonly dialog = inject(DialogService)

get immutable(): boolean {
return 'immutable' in this.spec && this.spec.immutable
Expand All @@ -50,9 +52,9 @@ export class FormControlComponent<
const previous = this.value

if (!this.warned && this.warning) {
this.dialog
.openAlert<boolean>(this.warning as unknown as i18nKey, {
label: 'Warning',
this.alerts
.open<boolean>(this.warning, {
label: this.i18n.transform('Warning'),
appearance: 'warning',
closeable: false,
autoClose: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
type="checkbox"
size="m"
[disabled]="!!spec.disabled || readOnly"
[showIcons]="false"
[(ngModel)]="value"
(blur)="onFocus(false)"
/>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { getMenu } from 'src/app/utils/system-utilities'
[tuiHintShowDelay]="1000"
[routerLink]="item.routerLink"
[class.link_system]="item.routerLink === '/portal/system'"
[tuiHint]="!rla.isActive ? item.name : ''"
[tuiHint]="rla.isActive ? '' : (item.name | i18n)"
>
<tui-badged-content
[style.--tui-radius.%]="50"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ import {
inject,
input,
} from '@angular/core'
import {
CopyService,
DialogService,
i18nKey,
i18nPipe,
} from '@start9labs/shared'
import { CopyService, DialogService, i18nPipe } from '@start9labs/shared'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import {
TuiButton,
tuiButtonOptionsProvider,
Expand Down Expand Up @@ -114,6 +110,7 @@ import { InterfaceComponent } from './interface.component'
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InterfaceActionsComponent {
readonly isMobile = inject(TUI_IS_MOBILE)
readonly dialog = inject(DialogService)
readonly copyService = inject(CopyService)
readonly interface = inject(InterfaceComponent)
Expand All @@ -124,7 +121,7 @@ export class InterfaceActionsComponent {
this.dialog
.openComponent(new PolymorpheusComponent(QRModal), {
size: 'auto',
label: this.actions() as i18nKey,
closeable: this.isMobile,
data: this.actions(),
})
.subscribe()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
import { toAcmeName } from 'src/app/utils/acme'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
import { InterfaceActionsComponent } from './actions.component'
import { AddressDetails } from './interface.utils'
import { ClearnetAddress } from './interface.utils'
import { MaskPipe } from './mask.pipe'

type ClearnetForm = {
Expand Down Expand Up @@ -85,25 +85,34 @@ type ClearnetForm = {
<table [appTable]="['ACME', 'URL', null]">
@for (address of clearnet(); track $index) {
<tr>
<td [style.width.rem]="12">{{ address.acme | acme }}</td>
<td [style.width.rem]="12">
{{
interface.serviceInterface().addSsl
? (address.acme | acme)
: '-'
}}
</td>
<td>{{ address.url | mask }}</td>
<td [actions]="address.url">
<button
tuiButton
appearance="primary-destructive"
[style.margin-inline-end.rem]="0.5"
(click)="remove(address)"
>
{{ 'Delete' | i18n }}
</button>
<button
tuiOption
tuiAppearance="action-destructive"
iconStart="@tui.trash"
(click)="remove(address)"
>
{{ 'Delete' | i18n }}
</button>
@if (address.isDomain) {
<button
tuiButton
appearance="primary-destructive"
[style.margin-inline-end.rem]="0.5"
(click)="remove(address)"
>
{{ 'Delete' | i18n }}
</button>
<!-- @TODO Alex I can resolve the issue below by wrapping each button in its own @if block, but that seems silly -->
<button
tuiOption
tuiAppearance="action-destructive"
iconStart="@tui.trash"
(click)="remove(address)"
>
{{ 'Delete' | i18n }}
</button>
}
</td>
</tr>
}
Expand Down Expand Up @@ -144,15 +153,15 @@ export class InterfaceClearnetComponent {
readonly interface = inject(InterfaceComponent)
readonly isPublic = computed(() => this.interface.serviceInterface().public)

readonly clearnet = input.required<readonly AddressDetails[]>()
readonly clearnet = input.required<readonly ClearnetAddress[]>()
readonly acme = toSignal(
inject<PatchDB<DataModel>>(PatchDB)
.watch$('serverInfo', 'network', 'acme')
.pipe(map(acme => Object.keys(acme))),
{ initialValue: [] },
)

async remove({ url }: AddressDetails) {
async remove({ url }: ClearnetAddress) {
const confirm = await firstValueFrom(
this.dialog
.openConfirm({ label: 'Are you sure?', size: 's' })
Expand Down Expand Up @@ -213,33 +222,37 @@ export class InterfaceClearnetComponent {
}

async add() {
const domain = ISB.Value.text({
name: 'Domain',
description: 'The domain or subdomain you want to use',
placeholder: `e.g. 'mydomain.com' or 'sub.mydomain.com'`,
required: true,
default: null,
patterns: [utils.Patterns.domain],
})
const acme = ISB.Value.select({
name: 'ACME Provider',
description:
'Select which ACME provider to use for obtaining your SSL certificate. Add new ACME providers in the System tab. Optionally use your system Root CA. Note: only devices that have trusted your Root CA will be able to access the domain without security warnings.',
values: this.acme().reduce(
(obj, url) => ({
...obj,
[url]: toAcmeName(url),
}),
{ none: 'None (use system Root CA)' } as Record<string, string>,
),
default: '',
})

this.formDialog.open<FormContext<ClearnetForm>>(FormComponent, {
label: 'Select Domain',
data: {
spec: await configBuilderToSpec(
ISB.InputSpec.of({
domain: ISB.Value.text({
name: 'Domain',
description: 'The domain or subdomain you want to use',
placeholder: `e.g. 'mydomain.com' or 'sub.mydomain.com'`,
required: true,
default: null,
patterns: [utils.Patterns.domain],
}),
acme: ISB.Value.select({
name: 'ACME Provider',
description:
'Select which ACME provider to use for obtaining your SSL certificate. Add new ACME providers in the System tab. Optionally use your system Root CA. Note: only devices that have trusted your Root CA will be able to access the domain without security warnings.',
values: this.acme().reduce(
(obj, url) => ({
...obj,
[url]: toAcmeName(url),
}),
{ none: 'None (use system Root CA)' } as Record<string, string>,
),
default: '',
}),
}),
ISB.InputSpec.of(
this.interface.serviceInterface().addSsl
? { domain, acme }
: { domain },
),
),
buttons: [
{
Expand Down
Loading