chore: fix issues from dev channel (#2968)

This commit is contained in:
Alex Inkin
2025-06-25 20:30:28 +07:00
committed by GitHub
parent dbf08a6cf8
commit 2983b9950f
12 changed files with 77 additions and 44 deletions

View File

@@ -521,4 +521,5 @@ export default {
520: 'Update verfügbar', 520: 'Update verfügbar',
521: 'Um das Problem zu beheben, siehe', 521: 'Um das Problem zu beheben, siehe',
522: 'SDK Version', 522: 'SDK Version',
523: 'Sicherungsbericht',
} satisfies i18n } satisfies i18n

View File

@@ -520,4 +520,5 @@ export const ENGLISH = {
'Update available': 520, 'Update available': 520,
'To resolve the issue, refer to': 521, 'To resolve the issue, refer to': 521,
'SDK Version': 522, 'SDK Version': 522,
'Backup Report': 523,
} as const } as const

View File

@@ -521,4 +521,5 @@ export default {
520: 'Actualización disponible', 520: 'Actualización disponible',
521: 'Para resolver el problema, consulta', 521: 'Para resolver el problema, consulta',
522: 'Versión de SDK', 522: 'Versión de SDK',
523: 'Informe de respaldo',
} satisfies i18n } satisfies i18n

View File

@@ -521,4 +521,5 @@ export default {
520: 'Mise à jour disponible', 520: 'Mise à jour disponible',
521: 'Pour résoudre le problème, consultez', 521: 'Pour résoudre le problème, consultez',
522: 'Version de SDK', 522: 'Version de SDK',
523: 'Rapport de sauvegarde',
} satisfies i18n } satisfies i18n

View File

@@ -521,4 +521,5 @@ export default {
520: 'Aktualizacja dostępna', 520: 'Aktualizacja dostępna',
521: 'Aby rozwiązać problem, zapoznaj się z', 521: 'Aby rozwiązać problem, zapoznaj się z',
522: 'Wersja SDK', 522: 'Wersja SDK',
523: 'Raport kopii zapasowej',
} satisfies i18n } satisfies i18n

View File

@@ -1,10 +1,13 @@
import { inject, Injectable, TemplateRef } from '@angular/core' import { inject, Injectable } from '@angular/core'
import { import {
TuiResponsiveDialogOptions, TuiResponsiveDialogOptions,
TuiResponsiveDialogService, TuiResponsiveDialogService,
} from '@taiga-ui/addon-mobile' } from '@taiga-ui/addon-mobile'
import { TUI_CONFIRM, TuiConfirmData } from '@taiga-ui/kit' import { TUI_CONFIRM, TuiConfirmData } from '@taiga-ui/kit'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus' import {
PolymorpheusComponent,
PolymorpheusContent,
} from '@taiga-ui/polymorpheus'
import { PROMPT, PromptOptions } from '../components/prompt.component' import { PROMPT, PromptOptions } from '../components/prompt.component'
import { i18nPipe } from '../i18n/i18n.pipe' import { i18nPipe } from '../i18n/i18n.pipe'
import { i18nKey } from '../i18n/i18n.providers' import { i18nKey } from '../i18n/i18n.providers'
@@ -73,7 +76,7 @@ export class DialogService {
} }
openComponent<T = void>( openComponent<T = void>(
component: PolymorpheusComponent<any> | TemplateRef<any>, component: PolymorpheusContent,
options: Partial<TuiResponsiveDialogOptions<any>> & { options: Partial<TuiResponsiveDialogOptions<any>> & {
label?: i18nKey label?: i18nKey
} = {}, } = {},

View File

@@ -18,8 +18,16 @@ export abstract class Control<
} }
get readOnly(): boolean { get readOnly(): boolean {
const def =
'default' in this.spec &&
this.spec.default != null &&
this.spec.default !== this.value
return ( return (
!!this.value && !!this.control.control?.pristine && this.control.immutable !!this.value &&
!def &&
!!this.control.control?.pristine &&
this.control.immutable
) )
} }

View File

@@ -1,4 +1,3 @@
import { TuiButton } from '@taiga-ui/core'
import { CommonModule, TitleCasePipe } from '@angular/common' import { CommonModule, TitleCasePipe } from '@angular/common'
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
@@ -6,37 +5,41 @@ import {
inject, inject,
Input, Input,
} from '@angular/core' } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { MarketplacePkgBase } from '@start9labs/marketplace' import { MarketplacePkgBase } from '@start9labs/marketplace'
import { import {
Exver,
ErrorService, ErrorService,
Exver,
ExverPipesModule,
i18nPipe,
isEmptyObject, isEmptyObject,
LoadingService, LoadingService,
sameUrl, sameUrl,
ExverPipesModule,
i18nPipe,
} from '@start9labs/shared' } from '@start9labs/shared'
import { TuiButton } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client' import { PatchDB } from 'patch-db-client'
import { firstValueFrom } from 'rxjs' import { firstValueFrom } from 'rxjs'
import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import { import {
DataModel, DataModel,
PackageDataEntry, PackageDataEntry,
} from 'src/app/services/patch-db/data-model' } from 'src/app/services/patch-db/data-model'
import { MarketplaceService } from 'src/app/services/marketplace.service'
import { hasCurrentDeps } from 'src/app/utils/has-deps'
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
import { dryUpdate } from 'src/app/utils/dry-update' import { dryUpdate } from 'src/app/utils/dry-update'
import { getAllPackages, getManifest } from 'src/app/utils/get-package-data'
import { hasCurrentDeps } from 'src/app/utils/has-deps'
import { MarketplacePreviewComponent } from '../modals/preview.component'
import { MarketplaceAlertsService } from '../services/alerts.service' import { MarketplaceAlertsService } from '../services/alerts.service'
import { ToManifestPipe } from 'src/app/routes/portal/pipes/to-manifest'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@Component({ @Component({
selector: 'marketplace-controls', selector: 'marketplace-controls',
template: ` template: `
@if (localPkg) { @if (localPkg) {
@if (localPkg | toManifest; as localManifest) { @if (localPkg | toManifest; as localManifest) {
@switch (localManifest.version | compareExver: pkg.version) { @switch (localManifest.version | compareExver: version() || '') {
@case (1) { @case (1) {
<button <button
tuiButton tuiButton
@@ -111,6 +114,9 @@ export class MarketplaceControlsComponent {
private readonly router = inject(Router) private readonly router = inject(Router)
private readonly marketplaceService = inject(MarketplaceService) private readonly marketplaceService = inject(MarketplaceService)
private readonly api = inject(ApiService) private readonly api = inject(ApiService)
private readonly preview = inject(MarketplacePreviewComponent)
protected readonly version = toSignal(this.preview.version$)
@Input({ required: true }) @Input({ required: true })
pkg!: MarketplacePkgBase pkg!: MarketplacePkgBase
@@ -147,10 +153,11 @@ export class MarketplaceControlsComponent {
} }
const localManifest = getManifest(this.localPkg) const localManifest = getManifest(this.localPkg)
const version = this.version() || ''
if ( if (
hasCurrentDeps(localManifest.id, await getAllPackages(this.patch)) && hasCurrentDeps(localManifest.id, await getAllPackages(this.patch)) &&
this.exver.compareExver(localManifest.version, this.pkg.version) !== 0 this.exver.compareExver(localManifest.version, version) !== 0
) { ) {
this.dryInstall(currentUrl) this.dryInstall(currentUrl)
} else { } else {
@@ -159,12 +166,14 @@ export class MarketplaceControlsComponent {
} }
async showService() { async showService() {
this.router.navigate(['/portal/services', this.pkg.id]) this.router.navigate(['/portal/services', this.preview.pkgId])
} }
private async dryInstall(url: string | null) { private async dryInstall(url: string | null) {
const id = this.preview.pkgId
const version = this.version() || ''
const breakages = dryUpdate( const breakages = dryUpdate(
this.pkg, { id, version },
await getAllPackages(this.patch), await getAllPackages(this.patch),
this.exver, this.exver,
) )
@@ -188,7 +197,8 @@ export class MarketplaceControlsComponent {
private async install(url: string) { private async install(url: string) {
const loader = this.loader.open('Beginning install').subscribe() const loader = this.loader.open('Beginning install').subscribe()
const { id, version } = this.pkg const version = this.version() || ''
const id = this.preview.pkgId
try { try {
await this.marketplaceService.installPackage(id, version, url) await this.marketplaceService.installPackage(id, version, url)

View File

@@ -167,11 +167,11 @@ export class MarketplacePreviewComponent {
private readonly exver = inject(Exver) private readonly exver = inject(Exver)
private readonly router = inject(Router) private readonly router = inject(Router)
private readonly marketplaceService = inject(MarketplaceService) private readonly marketplaceService = inject(MarketplaceService)
private readonly version$ = new BehaviorSubject<string | null>(null)
private readonly flavor$ = this.router.routerState.root.queryParamMap.pipe( private readonly flavor$ = this.router.routerState.root.queryParamMap.pipe(
map(paramMap => paramMap.get('flavor')), map(paramMap => paramMap.get('flavor')),
) )
readonly version$ = new BehaviorSubject<string | null>(null)
readonly pkg$ = combineLatest([this.version$, this.flavor$]).pipe( readonly pkg$ = combineLatest([this.version$, this.flavor$]).pipe(
switchMap(([version, flavor]) => switchMap(([version, flavor]) =>
this.marketplaceService this.marketplaceService

View File

@@ -112,7 +112,7 @@ import { SystemWipeComponent } from './wipe.component'
<tui-icon icon="@tui.languages" /> <tui-icon icon="@tui.languages" />
<span tuiTitle> <span tuiTitle>
<strong>{{ 'Language' | i18n }}</strong> <strong>{{ 'Language' | i18n }}</strong>
<span tuiSubtitle> <span tuiSubtitle [style.text-transform]="'capitalize'">
@if (language; as lang) { @if (language; as lang) {
{{ lang | i18n }} {{ lang | i18n }}
} @else { } @else {
@@ -197,7 +197,7 @@ import { SystemWipeComponent } from './wipe.component'
</div> </div>
} }
<img <img
[snek]="score()" [snek]="score() || 0"
class="snek" class="snek"
alt="Play Snake" alt="Play Snake"
src="assets/img/icons/snek.png" src="assets/img/icons/snek.png"
@@ -271,15 +271,17 @@ export default class SystemGeneralComponent {
readonly server = toSignal(this.patch.watch$('serverInfo')) readonly server = toSignal(this.patch.watch$('serverInfo'))
readonly name = toSignal(this.patch.watch$('ui', 'name')) readonly name = toSignal(this.patch.watch$('ui', 'name'))
readonly score = toSignal(this.patch.watch$('ui', 'snakeHighScore'))
readonly os = inject(OSService) readonly os = inject(OSService)
readonly i18nService = inject(i18nService) readonly i18nService = inject(i18nService)
readonly languages = languages readonly languages = languages
readonly translation: TuiStringHandler<TuiContext<Languages>> = ({ readonly translation: TuiStringHandler<TuiContext<Languages>> = ({
$implicit, $implicit,
}) => this.i18n.transform($implicit)! }) => {
readonly score = toSignal(this.patch.watch$('ui', 'snakeHighScore'), { const [head = '', ...result] = this.i18n.transform($implicit) || ''
initialValue: 0,
}) return [head.toUpperCase(), ...result].join('')
}
get language(): Languages | undefined { get language(): Languages | undefined {
return this.languages.find(lang => lang === this.i18nService.language) return this.languages.find(lang => lang === this.i18nService.language)

View File

@@ -9,18 +9,6 @@ export class FormDialogService {
private readonly dialog = inject(DialogService) private readonly dialog = inject(DialogService)
private readonly i18n = inject(i18nPipe) private readonly i18n = inject(i18nPipe)
private readonly formService = new TuiConfirmService() private readonly formService = new TuiConfirmService()
private readonly PROMPT: Partial<TuiDialogOptions<TuiConfirmData>> = {
label: this.i18n.transform('Unsaved changes'),
data: {
content: this.i18n.transform(
'You have unsaved changes. Are you sure you want to leave?',
),
yes: this.i18n.transform('Leave'),
no: this.i18n.transform('Cancel'),
},
}
private readonly prompt = this.formService.withConfirm(this.PROMPT)
private readonly injector = Injector.create({ private readonly injector = Injector.create({
parent: inject(Injector), parent: inject(Injector),
providers: [ providers: [
@@ -37,10 +25,23 @@ export class FormDialogService {
label?: i18nKey label?: i18nKey
} = {}, } = {},
) { ) {
const PROMPT: Partial<TuiDialogOptions<TuiConfirmData>> = {
label: this.i18n.transform('Unsaved changes'),
data: {
content: this.i18n.transform(
'You have unsaved changes. Are you sure you want to leave?',
),
yes: this.i18n.transform('Leave'),
no: this.i18n.transform('Cancel'),
},
}
const closeable = this.formService.withConfirm(PROMPT)
this.dialog this.dialog
.openComponent(new PolymorpheusComponent(component, this.injector), { .openComponent(new PolymorpheusComponent(component, this.injector), {
closeable: this.prompt, dismissible: closeable,
dismissible: this.prompt, closeable,
...options, ...options,
}) })
.subscribe({ .subscribe({

View File

@@ -1,6 +1,10 @@
import { inject, Injectable } from '@angular/core' import { inject, Injectable } from '@angular/core'
import { ErrorService, MARKDOWN } from '@start9labs/shared' import {
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile' DialogService,
ErrorService,
i18nKey,
MARKDOWN,
} from '@start9labs/shared'
import { PatchDB } from 'patch-db-client' import { PatchDB } from 'patch-db-client'
import { firstValueFrom, merge, of, shareReplay, Subject } from 'rxjs' import { firstValueFrom, merge, of, shareReplay, Subject } from 'rxjs'
import { REPORT } from 'src/app/components/backup-report.component' import { REPORT } from 'src/app/components/backup-report.component'
@@ -16,7 +20,7 @@ export class NotificationService {
private readonly patch = inject<PatchDB<DataModel>>(PatchDB) private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly errorService = inject(ErrorService) private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService) private readonly api = inject(ApiService)
private readonly dialogs = inject(TuiResponsiveDialogService) private readonly dialogs = inject(DialogService)
private readonly localUnreadCount$ = new Subject<number>() private readonly localUnreadCount$ = new Subject<number>()
readonly unreadCount$ = merge( readonly unreadCount$ = merge(
@@ -93,12 +97,12 @@ export class NotificationService {
{ data, createdAt, code, title, message }: ServerNotification<number>, { data, createdAt, code, title, message }: ServerNotification<number>,
full = false, full = false,
) { ) {
const label = code === 1 ? 'Backup Report' : title const label = code === 1 ? 'Backup Report' : (title as i18nKey)
const component = code === 1 ? REPORT : MARKDOWN const component = code === 1 ? REPORT : MARKDOWN
const content = code === 1 ? data : of(data) const content = code === 1 ? data : of(data)
this.dialogs this.dialogs
.open(full ? message : component, { .openComponent(full ? message : component, {
label, label,
data: { data: {
content, content,