mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 20:43:41 +00:00
Compare commits
3 Commits
v0.4.0-alp
...
plugins
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ed7660f9b | ||
|
|
886aa5d7db | ||
|
|
653a0a1428 |
@@ -399,7 +399,6 @@ export default {
|
|||||||
425: 'Ausführen',
|
425: 'Ausführen',
|
||||||
426: 'Aktion kann nur ausgeführt werden, wenn der Dienst',
|
426: 'Aktion kann nur ausgeführt werden, wenn der Dienst',
|
||||||
427: 'Verboten',
|
427: 'Verboten',
|
||||||
428: 'kann vorübergehend Probleme verursachen',
|
|
||||||
429: 'hat unerfüllte Abhängigkeiten. Es wird nicht wie erwartet funktionieren.',
|
429: 'hat unerfüllte Abhängigkeiten. Es wird nicht wie erwartet funktionieren.',
|
||||||
430: 'Container wird neu gebaut',
|
430: 'Container wird neu gebaut',
|
||||||
431: 'Deinstallation wird gestartet',
|
431: 'Deinstallation wird gestartet',
|
||||||
|
|||||||
@@ -398,7 +398,6 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Run': 425, // as in, run a piece of software
|
'Run': 425, // as in, run a piece of software
|
||||||
'Action can only be executed when service is': 426,
|
'Action can only be executed when service is': 426,
|
||||||
'Forbidden': 427,
|
'Forbidden': 427,
|
||||||
'may temporarily experiences issues': 428,
|
|
||||||
'has unmet dependencies. It will not work as expected.': 429,
|
'has unmet dependencies. It will not work as expected.': 429,
|
||||||
'Rebuilding container': 430,
|
'Rebuilding container': 430,
|
||||||
'Beginning uninstall': 431,
|
'Beginning uninstall': 431,
|
||||||
|
|||||||
@@ -399,7 +399,6 @@ export default {
|
|||||||
425: 'Ejecutar',
|
425: 'Ejecutar',
|
||||||
426: 'La acción solo se puede ejecutar cuando el servicio está',
|
426: 'La acción solo se puede ejecutar cuando el servicio está',
|
||||||
427: 'Prohibido',
|
427: 'Prohibido',
|
||||||
428: 'puede experimentar problemas temporales',
|
|
||||||
429: 'tiene dependencias no satisfechas. No funcionará como se espera.',
|
429: 'tiene dependencias no satisfechas. No funcionará como se espera.',
|
||||||
430: 'Reconstruyendo contenedor',
|
430: 'Reconstruyendo contenedor',
|
||||||
431: 'Iniciando desinstalación',
|
431: 'Iniciando desinstalación',
|
||||||
|
|||||||
@@ -399,7 +399,6 @@ export default {
|
|||||||
425: 'Exécuter',
|
425: 'Exécuter',
|
||||||
426: 'Action possible uniquement lorsque le service est',
|
426: 'Action possible uniquement lorsque le service est',
|
||||||
427: 'Interdit',
|
427: 'Interdit',
|
||||||
428: 'peut rencontrer des problèmes temporaires',
|
|
||||||
429: 'a des dépendances non satisfaites. Il ne fonctionnera pas comme prévu.',
|
429: 'a des dépendances non satisfaites. Il ne fonctionnera pas comme prévu.',
|
||||||
430: 'Reconstruction du conteneur',
|
430: 'Reconstruction du conteneur',
|
||||||
431: 'Désinstallation initiée',
|
431: 'Désinstallation initiée',
|
||||||
|
|||||||
@@ -399,7 +399,6 @@ export default {
|
|||||||
425: 'Uruchom',
|
425: 'Uruchom',
|
||||||
426: 'Akcja może być wykonana tylko gdy serwis jest',
|
426: 'Akcja może być wykonana tylko gdy serwis jest',
|
||||||
427: 'Zabronione',
|
427: 'Zabronione',
|
||||||
428: 'może tymczasowo napotkać problemy',
|
|
||||||
429: 'ma niespełnione zależności. Nie będzie działać zgodnie z oczekiwaniami.',
|
429: 'ma niespełnione zależności. Nie będzie działać zgodnie z oczekiwaniami.',
|
||||||
430: 'Odbudowywanie kontenera',
|
430: 'Odbudowywanie kontenera',
|
||||||
431: 'Rozpoczynanie odinstalowania',
|
431: 'Rozpoczynanie odinstalowania',
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
VERSION,
|
VERSION,
|
||||||
WorkspaceConfig,
|
WorkspaceConfig,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
import { tuiObfuscateOptionsProvider } from '@taiga-ui/cdk'
|
import { TUI_WINDOW_SIZE, tuiObfuscateOptionsProvider } from '@taiga-ui/cdk'
|
||||||
import {
|
import {
|
||||||
provideTaiga,
|
provideTaiga,
|
||||||
TUI_DIALOGS_CLOSE,
|
TUI_DIALOGS_CLOSE,
|
||||||
@@ -67,11 +67,12 @@ import {
|
|||||||
PATCH_CACHE,
|
PATCH_CACHE,
|
||||||
PatchDbSource,
|
PatchDbSource,
|
||||||
} from 'src/app/services/patch-db/patch-db-source'
|
} from 'src/app/services/patch-db/patch-db-source'
|
||||||
|
import { PluginsService } from 'src/app/services/plugins.service'
|
||||||
import { StateService } from 'src/app/services/state.service'
|
import { StateService } from 'src/app/services/state.service'
|
||||||
import { StorageService } from 'src/app/services/storage.service'
|
import { StorageService } from 'src/app/services/storage.service'
|
||||||
import {
|
import {
|
||||||
DateTransformer,
|
|
||||||
DatetimeTransformer,
|
DatetimeTransformer,
|
||||||
|
DateTransformer,
|
||||||
} from 'src/app/utils/value-transformers'
|
} from 'src/app/utils/value-transformers'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
|
|
||||||
@@ -185,5 +186,9 @@ export const APP_CONFIG: ApplicationConfig = {
|
|||||||
desktopLarge: Infinity,
|
desktopLarge: Infinity,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: TUI_WINDOW_SIZE,
|
||||||
|
useExisting: PluginsService,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { HeaderStatusComponent } from './status.component'
|
|||||||
template: `
|
template: `
|
||||||
<header-navigation />
|
<header-navigation />
|
||||||
<div class="item item_center">
|
<div class="item item_center">
|
||||||
|
<ng-content />
|
||||||
<div class="mobile"><ng-container #vcr /></div>
|
<div class="mobile"><ng-container #vcr /></div>
|
||||||
</div>
|
</div>
|
||||||
<header-status class="item item_connection" />
|
<header-status class="item item_connection" />
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
||||||
|
import { FormsModule } from '@angular/forms'
|
||||||
|
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
|
||||||
|
import { TuiAnimated } from '@taiga-ui/cdk'
|
||||||
|
import { TuiButton, TuiPopup } from '@taiga-ui/core'
|
||||||
|
import { ResizerComponent } from 'src/app/routes/portal/components/resizer.component'
|
||||||
|
import { PluginsService } from 'src/app/services/plugins.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-plugins',
|
||||||
|
template: `
|
||||||
|
<button
|
||||||
|
tuiIconButton
|
||||||
|
iconStart="@tui.bot-message-square"
|
||||||
|
[appearance]="service.enabled() ? 'positive' : 'icon'"
|
||||||
|
(click)="service.enabled.set(!service.enabled())"
|
||||||
|
>
|
||||||
|
AI assistant
|
||||||
|
</button>
|
||||||
|
<aside
|
||||||
|
*tuiPopup="service.enabled()"
|
||||||
|
tuiAnimated
|
||||||
|
[class._mobile]="mobile"
|
||||||
|
[style.--plugins]="service.size() / 100"
|
||||||
|
(click.self)="onClick($any($event).layerX)"
|
||||||
|
>
|
||||||
|
Plugin placeholder
|
||||||
|
</aside>
|
||||||
|
<input
|
||||||
|
*tuiPopup="service.enabled()"
|
||||||
|
appResizer
|
||||||
|
type="range"
|
||||||
|
step="0.1"
|
||||||
|
[(ngModel)]="service.size"
|
||||||
|
/>
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
:host {
|
||||||
|
float: inline-end;
|
||||||
|
margin-inline-end: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[tuiIconButton] {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0 0 0 calc(320px + (100% - 640px) * var(--plugins));
|
||||||
|
display: flex;
|
||||||
|
place-content: center;
|
||||||
|
place-items: center;
|
||||||
|
margin: var(--bumper) var(--bumper) var(--bumper) 0;
|
||||||
|
background: color-mix(in hsl, var(--start9-base-2) 75%, transparent);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
transparent,
|
||||||
|
var(--tui-background-base)
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(1rem);
|
||||||
|
border-radius: var(--bumper);
|
||||||
|
|
||||||
|
--tui-from: translateX(100%);
|
||||||
|
|
||||||
|
&._mobile {
|
||||||
|
inset-inline-start: 20%;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px rgba(255, 255, 255, 0.25),
|
||||||
|
0 0 0 100vh rgb(0 0 0 / 50%);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: -100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tui-enter,
|
||||||
|
&.tui-leave {
|
||||||
|
animation-name: tuiSlide, tuiFade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tui-enter,
|
||||||
|
&.tui-leave {
|
||||||
|
animation-name: tuiSlide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [FormsModule, TuiButton, ResizerComponent, TuiPopup, TuiAnimated],
|
||||||
|
})
|
||||||
|
export class PluginsComponent {
|
||||||
|
protected readonly mobile = inject(WA_IS_MOBILE)
|
||||||
|
protected readonly service = inject(PluginsService)
|
||||||
|
|
||||||
|
protected onClick(layerX: number) {
|
||||||
|
if (layerX < 0 && this.mobile) {
|
||||||
|
this.service.enabled.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'input[type="range"][appResizer]',
|
||||||
|
template: '',
|
||||||
|
styles: `
|
||||||
|
@use '@taiga-ui/styles/utils' as taiga;
|
||||||
|
|
||||||
|
:host {
|
||||||
|
@include taiga.transition(color);
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
inset: 0 calc(320px - var(--bumper)) 0 calc(320px - 2 * var(--bumper));
|
||||||
|
appearance: none;
|
||||||
|
pointer-events: none;
|
||||||
|
background: none;
|
||||||
|
color: transparent;
|
||||||
|
outline: none;
|
||||||
|
cursor: ew-resize;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--tui-background-neutral-1-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-runnable-track {
|
||||||
|
block-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
block-size: 100%;
|
||||||
|
inline-size: calc(var(--bumper) * 3);
|
||||||
|
padding: var(--bumper);
|
||||||
|
appearance: none;
|
||||||
|
background: currentColor;
|
||||||
|
background-clip: content-box;
|
||||||
|
border: none;
|
||||||
|
pointer-events: auto;
|
||||||
|
border-radius: var(--tui-radius-l);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-moz-range-thumb {
|
||||||
|
block-size: 100%;
|
||||||
|
inline-size: calc(var(--bumper) * 3);
|
||||||
|
padding: var(--bumper);
|
||||||
|
appearance: none;
|
||||||
|
background: currentColor;
|
||||||
|
background-clip: content-box;
|
||||||
|
border: none;
|
||||||
|
pointer-events: auto;
|
||||||
|
border-radius: var(--tui-radius-l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ResizerComponent {}
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { RouterOutlet } from '@angular/router'
|
import { RouterOutlet } from '@angular/router'
|
||||||
|
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
|
||||||
import { ErrorService, i18nPipe } from '@start9labs/shared'
|
import { ErrorService, i18nPipe } from '@start9labs/shared'
|
||||||
import {
|
import {
|
||||||
TuiButton,
|
TuiButton,
|
||||||
@@ -21,15 +22,17 @@ import {
|
|||||||
TuiProgress,
|
TuiProgress,
|
||||||
} from '@taiga-ui/kit'
|
} from '@taiga-ui/kit'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
|
import { PluginsComponent } from 'src/app/routes/portal/components/plugins.component'
|
||||||
import { TabsComponent } from 'src/app/routes/portal/components/tabs.component'
|
import { TabsComponent } from 'src/app/routes/portal/components/tabs.component'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { OSService } from 'src/app/services/os.service'
|
import { OSService } from 'src/app/services/os.service'
|
||||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||||
|
import { PluginsService } from 'src/app/services/plugins.service'
|
||||||
import { HeaderComponent } from './components/header/header.component'
|
import { HeaderComponent } from './components/header/header.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<header appHeader>{{ name() }}</header>
|
<header appHeader><app-plugins /></header>
|
||||||
<main>
|
<main>
|
||||||
<tui-scrollbar [style.max-height.%]="100">
|
<tui-scrollbar [style.max-height.%]="100">
|
||||||
<router-outlet />
|
<router-outlet />
|
||||||
@@ -91,19 +94,47 @@ import { HeaderComponent } from './components/header/header.component'
|
|||||||
styles: `
|
styles: `
|
||||||
@use '@taiga-ui/styles/utils' as taiga;
|
@use '@taiga-ui/styles/utils' as taiga;
|
||||||
|
|
||||||
|
@keyframes open {
|
||||||
|
from {
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
inline-size: calc(320px + (100% - 640px) * var(--plugins));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
height: 100%;
|
@include taiga.transition(inline-size);
|
||||||
|
|
||||||
|
block-size: 100%;
|
||||||
|
inline-size: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// @TODO Theme
|
|
||||||
background: url(/assets/img/background_dark.jpeg) fixed center/cover;
|
|
||||||
|
|
||||||
&::before {
|
&._plugins {
|
||||||
|
inline-size: calc(320px + (100% - 640px) * var(--plugins));
|
||||||
|
animation: open var(--tui-duration) ease-in-out;
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
app-tabs {
|
||||||
|
inline-size: calc(100% - var(--bumper));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
backdrop-filter: blur(0.5rem);
|
backdrop-filter: blur(0.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
z-index: -1;
|
||||||
|
// @TODO Theme
|
||||||
|
background: url(/assets/img/background_dark.jpeg) fixed center/cover;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@@ -125,6 +156,10 @@ import { HeaderComponent } from './components/header/header.component'
|
|||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
host: {
|
||||||
|
'[class._plugins]': '!mobile && plugins.enabled()',
|
||||||
|
'[style.--plugins]': 'plugins.size() / 100',
|
||||||
|
},
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
RouterOutlet,
|
RouterOutlet,
|
||||||
@@ -139,6 +174,7 @@ import { HeaderComponent } from './components/header/header.component'
|
|||||||
TuiPopup,
|
TuiPopup,
|
||||||
TuiCell,
|
TuiCell,
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
|
PluginsComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PortalComponent {
|
export class PortalComponent {
|
||||||
@@ -147,6 +183,8 @@ export class PortalComponent {
|
|||||||
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
|
||||||
private readonly api = inject(ApiService)
|
private readonly api = inject(ApiService)
|
||||||
|
|
||||||
|
readonly mobile = inject(WA_IS_MOBILE)
|
||||||
|
readonly plugins = inject(PluginsService)
|
||||||
readonly name = toSignal(this.patch.watch$('serverInfo', 'name'))
|
readonly name = toSignal(this.patch.watch$('serverInfo', 'name'))
|
||||||
readonly update = toSignal(inject(OSService).updating$)
|
readonly update = toSignal(inject(OSService).updating$)
|
||||||
readonly restartReason = toSignal(
|
readonly restartReason = toSignal(
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import { InterfaceService } from '../../../components/interfaces/interface.servi
|
|||||||
<button
|
<button
|
||||||
tuiButton
|
tuiButton
|
||||||
iconStart="@tui.rotate-cw"
|
iconStart="@tui.rotate-cw"
|
||||||
(click)="controls.restart(manifest())"
|
(click)="controls.restart(manifest().id)"
|
||||||
>
|
>
|
||||||
{{ 'Restart' | i18n }}
|
{{ 'Restart' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { RouterLink } from '@angular/router'
|
|||||||
import { MarketplacePkg } from '@start9labs/marketplace'
|
import { MarketplacePkg } from '@start9labs/marketplace'
|
||||||
import {
|
import {
|
||||||
DialogService,
|
DialogService,
|
||||||
i18nKey,
|
|
||||||
i18nPipe,
|
i18nPipe,
|
||||||
LocalizePipe,
|
LocalizePipe,
|
||||||
MarkdownPipe,
|
MarkdownPipe,
|
||||||
@@ -32,7 +31,6 @@ import {
|
|||||||
TuiProgressCircle,
|
TuiProgressCircle,
|
||||||
} from '@taiga-ui/kit'
|
} from '@taiga-ui/kit'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { defaultIfEmpty, firstValueFrom } from 'rxjs'
|
|
||||||
import { InstallingProgressPipe } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
import { InstallingProgressPipe } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
|
||||||
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
import { MarketplaceService } from 'src/app/services/marketplace.service'
|
||||||
import {
|
import {
|
||||||
@@ -41,8 +39,6 @@ import {
|
|||||||
PackageDataEntry,
|
PackageDataEntry,
|
||||||
UpdatingState,
|
UpdatingState,
|
||||||
} from 'src/app/services/patch-db/data-model'
|
} from 'src/app/services/patch-db/data-model'
|
||||||
import { getAllPackages } from 'src/app/utils/get-package-data'
|
|
||||||
import { hasCurrentDeps } from 'src/app/utils/has-deps'
|
|
||||||
import UpdatesComponent from './updates.component'
|
import UpdatesComponent from './updates.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -106,7 +102,7 @@ import UpdatesComponent from './updates.component'
|
|||||||
size="s"
|
size="s"
|
||||||
[loading]="!ready()"
|
[loading]="!ready()"
|
||||||
[appearance]="error() ? 'destructive' : 'primary'"
|
[appearance]="error() ? 'destructive' : 'primary'"
|
||||||
(click.stop)="onClick()"
|
(click.stop)="update()"
|
||||||
>
|
>
|
||||||
{{ error() ? ('Retry' | i18n) : ('Update' | i18n) }}
|
{{ error() ? ('Retry' | i18n) : ('Update' | i18n) }}
|
||||||
</button>
|
</button>
|
||||||
@@ -274,22 +270,7 @@ export class UpdatesItemComponent {
|
|||||||
readonly local =
|
readonly local =
|
||||||
input.required<PackageDataEntry<InstalledState | UpdatingState>>()
|
input.required<PackageDataEntry<InstalledState | UpdatingState>>()
|
||||||
|
|
||||||
async onClick() {
|
async update() {
|
||||||
this.ready.set(false)
|
|
||||||
this.error.set('')
|
|
||||||
|
|
||||||
if (hasCurrentDeps(this.item().id, await getAllPackages(this.patch))) {
|
|
||||||
if (await this.alert()) {
|
|
||||||
await this.update()
|
|
||||||
} else {
|
|
||||||
this.ready.set(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async update() {
|
|
||||||
const { id, version } = this.item()
|
const { id, version } = this.item()
|
||||||
const url = this.parent.current()?.url || ''
|
const url = this.parent.current()?.url || ''
|
||||||
|
|
||||||
@@ -301,21 +282,4 @@ export class UpdatesItemComponent {
|
|||||||
this.error.set(e.message)
|
this.error.set(e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async alert(): Promise<boolean> {
|
|
||||||
return firstValueFrom(
|
|
||||||
this.dialog
|
|
||||||
.openConfirm({
|
|
||||||
label: 'Warning',
|
|
||||||
size: 's',
|
|
||||||
data: {
|
|
||||||
content:
|
|
||||||
`${this.i18n.transform('Services that depend on')} ${this.local().stateInfo.manifest.title} ${this.i18n.transform('will no longer work properly and may crash.')}` as i18nKey,
|
|
||||||
yes: 'Continue',
|
|
||||||
no: 'Cancel',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(defaultIfEmpty(false)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,35 +84,16 @@ export class ControlsService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async restart({ id, title }: T.Manifest) {
|
async restart(id: string) {
|
||||||
const packages = await getAllPackages(this.patch)
|
const loader = this.loader.open('Restarting').subscribe()
|
||||||
|
|
||||||
defer(() =>
|
try {
|
||||||
hasCurrentDeps(id, packages)
|
await this.api.restartPackage({ id })
|
||||||
? this.dialog
|
} catch (e: any) {
|
||||||
.openConfirm({
|
this.errorService.handleError(e)
|
||||||
label: 'Warning',
|
} finally {
|
||||||
size: 's',
|
loader.unsubscribe()
|
||||||
data: {
|
}
|
||||||
content:
|
|
||||||
`${this.i18n.transform('Services that depend on')} ${title} ${this.i18n.transform('may temporarily experiences issues')}` as i18nKey,
|
|
||||||
yes: 'Restart',
|
|
||||||
no: 'Cancel',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.pipe(filter(Boolean))
|
|
||||||
: of(null),
|
|
||||||
).subscribe(async () => {
|
|
||||||
const loader = this.loader.open('Restarting').subscribe()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.api.restartPackage({ id })
|
|
||||||
} catch (e: any) {
|
|
||||||
this.errorService.handleError(e)
|
|
||||||
} finally {
|
|
||||||
loader.unsubscribe()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private alert(content: T.LocaleString): Promise<boolean> {
|
private alert(content: T.LocaleString): Promise<boolean> {
|
||||||
|
|||||||
37
web/projects/ui/src/app/services/plugins.service.ts
Normal file
37
web/projects/ui/src/app/services/plugins.service.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { inject, Injectable, INJECTOR, Injector, signal } from '@angular/core'
|
||||||
|
import { toObservable } from '@angular/core/rxjs-interop'
|
||||||
|
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
|
||||||
|
import { TUI_WINDOW_SIZE } from '@taiga-ui/cdk'
|
||||||
|
import { TUI_MEDIA } from '@taiga-ui/core'
|
||||||
|
import { combineLatest, map, Observable } from 'rxjs'
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class PluginsService extends Observable<DOMRect> {
|
||||||
|
public readonly enabled = signal(false)
|
||||||
|
public readonly size = signal(100)
|
||||||
|
|
||||||
|
// @ts-expect-error triggering TUI_WINDOW_SIZE default factory
|
||||||
|
private readonly window = Injector.create({
|
||||||
|
parent: inject(INJECTOR),
|
||||||
|
providers: [{ provide: TUI_WINDOW_SIZE }],
|
||||||
|
}).get(TUI_WINDOW_SIZE)
|
||||||
|
|
||||||
|
private readonly media = inject(TUI_MEDIA)
|
||||||
|
private readonly stream = inject(WA_IS_MOBILE)
|
||||||
|
? this.window
|
||||||
|
: combineLatest([
|
||||||
|
this.window,
|
||||||
|
toObservable(this.size),
|
||||||
|
toObservable(this.enabled),
|
||||||
|
]).pipe(
|
||||||
|
map(([window, size, enabled]) =>
|
||||||
|
window.width < this.media.mobile || !enabled
|
||||||
|
? window
|
||||||
|
: { ...window, width: 320 + (window.width - 640) * (size / 100) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(subscriber => this.stream.subscribe(subscriber))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user